循环队列与链队列的简单实现

一、循环队列

a、概念

    为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。


    通过上图可以看出,如果使用顺序表作为队列的话,当处于d状态则不能继续插入新的队尾元素,否则会因为数组越界而导致程序代码被破坏。


    所以产生了由链表实现的循环队列,只有队列未满时才可以插入新的队尾元素。判断循环队列是空队列还是满队列依据:头尾指针相同就是空,尾指针的下一个是头指针就是满。

b、代码及运行结果

#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define MAXQSIZE 5
typedef int Status;
typedef int QElemType;
typedef struct Node
{
    QElemType *base; //初始化动态分配存储空间
    int front;
    int rear;
} SqQueue;

Status InitQueue(SqQueue *Q)
{
    Q->base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType));
    if (!Q->base)
        return ERROR;
    Q->front = Q->rear = 0;
    return OK;
}

Status EnQueue(SqQueue *Q, QElemType elem)
{
    //队列为空时 1%5==1,队列满时(4+1)%5==0,最多容纳4个元素
    if ((Q->rear + 1) % MAXQSIZE == (Q->front))
        return ERROR;
    Q->base[Q->rear] = elem;
    Q->rear = (Q->rear + 1) % MAXQSIZE; //rear始终在0-5中循环
    return OK;
}

Status OutQueue(SqQueue *Q, QElemType *e)
{
    if (Q->front == Q->rear)
        return ERROR;
    *e = Q->base[Q->front];
    Q->front = (Q->front + 1) % MAXQSIZE;
    return OK;
}

Status PrintQueue(SqQueue Q)
{
    printf("the queue is:");
    for (int i = Q.front; i < Q.rear; ++i)
        printf("%d ", Q.base[i]);
    return OK;
}
int main()
{
    SqQueue queue;
    QElemType elem;
    int i;
    while(!InitQueue(&queue));
    EnQueue(&queue,1);
    EnQueue(&queue,2);
    EnQueue(&queue,3);
    EnQueue(&queue,4);
    EnQueue(&queue,5);//队列已满,无效入队
    printf("\noutput:");
    scanf("%d", &i);
    while(i != 0)
    {
        OutQueue(&queue, &elem);
        i--;
    }
    PrintQueue(queue);
    return OK;
}

二、链队列

a、概念

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

 当队列为空时,front和rear都指向头结点;

    

    入队:在队列尾部插入结点;

    

    出队:头结点的后继结点(队头)出队,将头结点的后继改为他后面的结点,若链表除头结点外只剩一个元素时,则需将rear指向头结点。

    一般情况下,链队列的出队图示:

    

b、代码及运行结果

  
#include<stdio.h>  

#include<stdlib.h>  

//链队列结点结构  

typedef struct QNode{  

    int data;  

    struct QNode *next;  

}QNode;  

//链队列结构  

typedef struct LiQueue{  

    QNode *front;  

    QNode *rear;  

}LiQueue;  

//创建链队列  

LiQueue initQueue(){  

    LiQueue *lq=(LiQueue *)malloc(sizeof(LiQueue));  

    if(lq ==NULL){  

        return *lq;  

    }  

    lq->front=lq->rear=NULL;  

    return *lq;  

}  

//判断链队列是否为空  

int isEmpty(LiQueue *lq){  

    if(lq->front==NULL || lq->rear==NULL){  

        return 0;  

    }else{  

        return 1;  

    }  

}  

//元素进链队列  

void enQueue(LiQueue *lq,int x){  

    QNode *p;  

    p=(QNode *)malloc(sizeof(QNode));  

    p->data=x;  

    p->next=NULL;  

    if(lq->rear==NULL){  

        lq->front=lq->rear=p;//如果第一次插入则设置头指针和尾指针为p  

    }else{  

        lq->rear->next=p;//链队列的尾部插入p  

        lq->rear=p;//设置链队列的尾指针指向p  

    }  

}  

//元素出链队列  

int deQueue(LiQueue *lq,int *y){  

    QNode *p=lq->front;  

    if(lq->rear==NULL || lq->front==NULL){  

        return 0;  

    }  

    if(lq->front==lq->rear){  

        lq->front=lq->rear=NULL;  

    }else{  

        lq->front=lq->front->next;  

    }  

    *y=p->data;  

    free(p);  

    return 1;  

}  

//打印链队列  

void printQueue(LiQueue lq){  

    if(lq.front==NULL || lq.rear==NULL){  

        return;  

    }  

    while(lq.front!=NULL){  

        printf("%d\n",lq.front->data);  

        lq.front=lq.front->next;  

    }  

}  

int main(void){  

    int y=0;  

    LiQueue lq=initQueue();  

    enQueue(&lq,1);  

    enQueue(&lq,2);  

    enQueue(&lq,3);  

    enQueue(&lq,4);  

    enQueue(&lq,5);  

    //deQueue(&lq,&y);  

    printQueue(lq);  

    printf("出队列元素=%d\n",y);  

return 0;

}  

三、循环队列和链队列的比较

 从时间上,其实它们的基本操作都是常数时间,即都为0(1)的,不过循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有细微差异。

 对于空间上来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管他需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。

 总的来说,在可以确定队列长度的情况下,建议用循环队列;不能预估队列长度时,建议用链队列。


阅读更多

没有更多推荐了,返回首页