1. 程式人生 > >迴圈佇列與鏈佇列的簡單實現

迴圈佇列與鏈佇列的簡單實現

一、迴圈佇列

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)的,不過迴圈佇列是事先申請好空間,使用期間不釋放,而對於鏈佇列,每次申請和釋放結點也會存在一些時間開銷,如果入隊出隊頻繁,則兩者還是有細微差異。

 對於空間上來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間浪費的問題。而鏈佇列不存在這個問題,儘管他需要一個指標域,會產生一些空間上的開銷,但也可以接受。所以在空間上,鏈佇列更加靈活。

 總的來說,在可以確定佇列長度的情況下,建議用迴圈佇列;不能預估佇列長度時,建議用鏈佇列。