1. 程式人生 > >佇列——鏈佇列和迴圈佇列

佇列——鏈佇列和迴圈佇列

鏈佇列

轉載:https://www.cnblogs.com/muzijie/p/5655228.html

1 鏈佇列的儲存結構

  將對頭指標front指向鏈佇列的頭結點,隊尾指標rear指向終端結點。

空佇列時,頭指標front和尾指標rear都指向頭結點。

 

鏈佇列的儲存結構為:

typedef int QElemType;
typedef struct QNode {            //結點結構
    QElemType data;
    struct QNode *next;
}QNode;

typedef struct QNode * QueuePtr;

typedef struct {                //佇列的連結串列結構
    QueuePtr rear;
    QueuePtr front;
}LinkQueue;

2 入隊操作

//插入元素e為Q的新的隊尾結點
Status EnQueue(QueuePtr Q, QElemType e) {
    QueuePtr q = (QueuePtr)malloc(sizeof(QNode));
    if (!q) {                //儲存分配失敗
        exit(OVERFLOW);
    }
    q->data = e;
    q->next = NULL;
    Q->rear->next = q;
    Q->rear = q;
    return OK;
}

3 出隊操作

  出隊操作,就是頭結點的後繼結點出隊,將頭結點的後繼改為它後面的結點。

  若連結串列除頭結點外只剩一個元素時,則需將rear指標指向頭結點。

//若佇列不空,刪除Q的隊頭元素,用e返回其值,並返回OK,否則返回ERROR。
Status DeQueue(QueuePtr Q, QElemType *e) {
    QueuePtr q;
    if (Q->rear == Q->front) {        //空佇列
        return ERROR;
    }
    q = Q->front->next;                //q指向第一個結點
    *e = q->data;
    Q->front->next = q->next;

    if (Q->rear == p) {                //若隊頭就是隊尾,刪除後,需要將rear指標指向頭結點
        Q->rear = Q->front;
    }
    free(q);
    return OK;
}

4 迴圈佇列與鏈佇列的比較

  從時間上考慮,迴圈佇列和鏈佇列的基本操作都是O(1),不過迴圈佇列是事先已申請好空間,使用期間不會釋放。而對於鏈佇列,每次申請和釋放結點也會存在一些時間開銷。如果入隊和出隊頻繁,兩者還是有細微差異的。
  從空間來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間浪費的問題。而鏈佇列不存在這個問題,儘管它需要一個指標域,會產生一些空間上的開銷,但是是可以接受的。所以從空間上說,鏈佇列更加靈活。
  總的來說,在可以確定鏈佇列最大長度的情況下,建議使用迴圈佇列。如果無法預估佇列的長度,則使用鏈佇列。

 

 

迴圈佇列

轉載:https://www.cnblogs.com/hughdong/archive/2017/05/11/6841970.html

(作者說的挺有意思的話:You know something and I know nothing.)

        和順序棧相類似,在佇列的順序儲存結構中,除了用一組地址連續的儲存單元依次存放從隊頭到隊尾的元素外,尚需敷設兩個指標front和rear分別指示佇列頭元素位置和佇列尾元素的位置。

如果使用順序表作為佇列的話,當處於右圖狀態則不能繼續插入新的隊尾元素,否則會因為陣列越界而導致程式程式碼被破壞。

 

由此產生了由連結串列實現的迴圈佇列,只有佇列未滿時才可以插入新的隊尾元素。

 

下面內容,轉載:https://www.cnblogs.com/chenliyang/p/6554141.html

        1.圖中有兩個指標(其實就是兩個整數型變數,因為在這裡有指示作用,所以這裡理解為指標)front、rear,一個指示隊頭,一個指示隊尾。

     2.rear和front互相追趕著,這個追趕過程就是佇列新增和刪除的過程,如果rear追到head說明佇列滿了,如果front追到rear說明佇列。

說明:令佇列空間中的一個單元閒置,使得佇列非空時,Q.rear與Q.front之間至少間隔一個空閒單。(思考為什麼空一格)

     3.我們把它掰彎,用的是求餘,這樣兩個值就不會跑出最大範圍,並且可以實現彎曲的效果,所以說對於迴圈佇列我們必須給定最大值MAXQSIZE。

   這其實是我們臆想的,反正我們要做的就是利用迴圈來解決空間浪費的問題。  

迴圈佇列的實現過程(important)

    ☆當新增一個元素時,(rear+1)%MAXQSIZE; //理解為什麼求餘?

    ☆當刪除一個元素時,(front+1)%MAXQSIZE;//理解為什麼求餘?

    ☆當rear=front的時候,佇列可能是滿,也可能是空。(這也是為什麼空一格的原因)

      因為存在滿和空兩種情況,我們需要分別判斷:

        ☆滿:當佇列新增元素到rear的下一個元素是head的時候,也就是轉圈子要碰頭了,我們就認為佇列滿了。(Q.rear+1)%MAXSIZE=Q.front

        ☆:當佇列刪除元素到head=rear的時候,我們認為佇列空了。Q.rear==Q.front,不一定為0

        上面這一段要好好理解,在其他程式設計的地方,也會用到類似的思想。

        下面的程式碼思想很重要。

2.1對節點的定義

#define MAXQSIZE 100
typedef int QElemtype;
typedef int status;

typedef struct{
    QElemtype *base;
    int front;
    int rear;
    }SqQueue;

2.2初始化佇列

SqQueue* InitQueue()
{
    SqQueue *q;
    q=new SqQueue;
    q->base=new int[MAXQSIZE];
    q->rear=q->front=0;
    return q;
}

2.3新增操作

status EnQueue(SqQueue *q,QElemtype e)
{
    //插入到隊尾
    if((q->rear+1)%MAXQSIZE==q->front)
        return 0;
    q->base[q->rear]=e;
    q->rear=(q->rear+1)%MAXQSIZE;
    return 1;
}

2.4刪除操作

status DeQueue(SqQueue *q)
{
    if(q->front==q->rear)
        return 0;
    printf("%d",q->base[q->front]);
    q->front =(q->front+1)%MAXQSIZE;
    return 1;
}

備註,這插入和刪除的操作,類似於標記。(這也很重要)

2.5獲取佇列長度

int QueueLength(SqQueue *q)
{
    return (q->rear-q->front+MAXQSIZE)%MAXQSIZE;
}

 

補:還有些其他的佇列,比如優先佇列,雙端佇列。(用的時候可以查STL)

佇列練習:

團體佇列

並行程式模擬