数据结构—循环队列与链队列

数据结构—循环队列与链队列

我们上一节已经提到过

栈和队列都是对插入和删除数据元素有具体要求的线性表

这一节我们就要介绍另一种数据结构:队列。


队列:是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

我们还是先举个例子,其实队列在我们的平时生活中最长见,食堂里排队打饭,后来的肯定是站在队伍最后,然后前面的人打好饭之后再离开;不可能后来的人站在最前面,这样就没人愿意去排队了。这其实就类似于队列的实现方式。
队列是一种先进先出的线性表。允许插入的一段叫队尾,允许删除的一端称为队头。
由于最普通的顺序结构队列几乎与前面讲过的顺序结构线性表相同,这里我们不再赘述,直接介绍效率更高的循环队列和链队列。


1.循环队列

如果我们使用的是普通顺序结构的队列,将数组下标为0的一端设为队头,最后一个数组元素的下标设为队尾,那么一个数据入队时,就只需要在队尾增加数组元素就可以。但是如果一个数据要出队时,在删去第一个数组元素后,后面的数据都要向前移动一位,我们知道这样的话时间复杂度会为O(n)。所以我们要想出一个更好的办法来解决时间效率低的问题。

  • 队头可不可以不放在第一个数组元素的位置?当然是可以的,我们在一个元素出队后,让对头向后移动一位就可以

  • 那前面出队后留下的空间怎么办呢?会不会造成浪费?我们可以这样做:在队尾到达数组最大限制后,让队尾回到第一个数组元素的位置,再向后推。

  • 这样做的话就会出现对头在队尾前队尾在对头前两种情况。问题又来了,如果表头表尾在同一位置时表示空队列的话,那么什么时候表示队列已经满了呢?下面是两种方法:

    1. 设置一个标志变量flag,当front == rear,且flag = 0时队列为空,当front == rear,且flag = 1时队列为满。
    2. 当队列空时,front == rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说当队列满时,数组中还有一个空闲单元。

几个重要判断条件:
队空:front == rear
队满:(rear +1)%MAX = front
队列长度:(rear - front +MAX)%MAX

我们将要用到的是第二种方法。
以下是循环队列的代码实现:

#include<stdio.h>
#include<stdlib.h>
#define MAX 20

typedef struct{
    int date[MAX];
    int front;
    int rear;
}SqQueue;

SqQueue *InitQueue(SqQueue *queue){
    queue = (SqQueue *)malloc(sizeof(SqQueue));
    queue->front = 0;
    queue->rear = 0;
    return queue;
}

void Enqueue(SqQueue *queue){
    if((queue->rear+1) % MAX == queue->front){
        printf("队列已满!\n");
        return;
    }
    printf("请输入入队数据:");
    scanf("%d",&queue->date[queue->rear]);
    queue->rear = (queue->rear+1) % MAX;
    printf("入队成功!\n");
}

void Dequeue(SqQueue *queue){
    if(queue->front == queue->rear){
        printf("队列为空!\n");
        return;
    }
    printf("你要删除的数据元素为:%d",queue->date[queue->front]);
    queue->front = (queue->front+1) % MAX;
    printf("出队成功!\n");
}

void print_queue(SqQueue *queue){
    if(queue->front == queue->rear){
        printf("目前队列为空!\n");
        return;
    }
    printf("目前队列中有%d个数据元素\n",(queue->rear-queue->front+MAX)%MAX);
    if(queue->front < queue->rear){
        int index = 1;
        for(int i = queue->front; i <queue->rear; i++){
            printf("第%d个数据为:",index++);
            printf("%d\n",queue->date[i]);
        }
    }
    else if(queue->front > queue->rear){
        int index = 1;
        int i;
        for(i = queue->front; i < MAX; i++){
            printf("第%d个数据为:",index++);
            printf("%d\n",queue->date[i]);
        }
        for(i = 0; i < queue->rear; i++){
            printf("第%d个数据为:",index++);
            printf("%d\n",queue->date[i]);
        }
    }
}

int main(){
    SqQueue *queue;
    queue = InitQueue(queue);
    int choice;
    while(1){
        printf("1.入队\n");
        printf("2.出队\n");
        printf("3.输出\n");
        scanf("%d",&choice);
        switch(choice){
            case 1:Enqueue(queue);
                break;
            case 2:Dequeue(queue);
                break;
            case 3:print_queue(queue);
                break;
        }
    }
    return 0;
}

2.链队列

队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它称为链队列。

当链队列为空时,front和rear都指向单链表的头节点。当有数据入队时,在链尾插入新节点,rear随之向后移动,当删除节点时,删除头节点后的第一个数据节点,注意:当删除的节点是链表中唯一一个数据节点时,删除之后链表中只留下了一个头节点,这时要让rear指向头节点。
以下是链队列的代码实现:

#include<stdio.h>
#include<stdlib.h>

typedef struct Qnode{
    int date;
    struct Qnode *next;
}Qnode,*ptr;

typedef struct{
    ptr front;
    ptr rear;
    int count;
}linkqueue;

linkqueue *Initqueue(linkqueue *queue){
    queue = (linkqueue *)malloc(sizeof(linkqueue));
    ptr pHead = (ptr)malloc(sizeof(Qnode));
    pHead->next = NULL;
    queue->front = pHead;
    queue->rear = pHead;
    queue->count = 0;
    return queue;
}

void Enqueue(linkqueue *queue){
    ptr pnew = (ptr)malloc(sizeof(Qnode));
    printf("请输入入队数据:");
    scanf("%d",&pnew->date);
    pnew->next = NULL;
    queue->rear->next = pnew;
    queue->rear = pnew;
    queue->count++;
    printf("入队成功!\n");
}

void Dequeue(linkqueue *queue){
    if(queue->front == queue->rear){
        printf("队列目前为空!\n");
        return;
    }
    ptr temp = queue->front->next;
    printf("你要删除的数据为:%d",temp->date);
    queue->front->next = temp->next;
    if(queue->rear == temp)
        queue->rear = queue->front;
    free(temp);
    queue->count--;
    printf("出队成功!\n");
}

void print_queue(linkqueue *queue){
    if(queue->front == queue->rear){
        printf("目前为空队列!\n");
        return;
    }
    printf("目前队列中共有%d个数据\n",queue->count);
    int index = 1;
    ptr temp = queue->front->next;
    while(temp != NULL){
        printf("第%d个数据:",index++);
        printf("%d\n",temp->date);
        temp = temp->next;
    }
}

int main(){
    linkqueue *queue;
    queue = Initqueue(queue);
    int choice;
    while(1){
        printf("1.入队\n");
        printf("2.出队\n");
        printf("3.输出\n");
        scanf("%d",&choice);
        switch(choice){
            case 1:Enqueue(queue);
                break;
            case 2:Dequeue(queue);
                break;
            case 3:print_queue(queue);
                break;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wintershii/article/details/80274934