數據結構與算法--棧、隊列(隊列)


Hello,everybody.我們又見面了。今天我們來學習一下隊列這個數據結構,let’s Go,開始我們的征程吧。

首先,舉兩個生活中的常見例子。相信大家,在用電腦工作娛樂時,都會碰到這樣的現象。當我們點擊程序或進行其他操作時,電腦處於死機狀態。正當我們准備Reset時,它突然像打了雞血似的,突然把剛才我們的操作,按順序執行了一遍。之所以會出現這個現象,是因為操作系統的多個程序,需要通過一個管道輸出,而按先后順序排隊造成的。

還有有個例子,在我們打客服熱線時,有時會出現等待的現象。當其他客戶掛斷電話,客服人員才會接通我們的電話。因為客服人員相對於客戶而言,總是不夠的,當客戶量大於客服人員時,就會造成排隊等待的想象。

操作系統、客服系統,都是應用了一種數據結構才實現了這種先進先出的排隊功能,這個數據結構就是隊列。

隊列(Queue):是只允許在一端進行插入操作,在另一端進行刪除操作的線性表。

隊列也是一種特殊的線性表,是一種先進先出的線性表。允許插入的一端稱為表尾,允許刪除的一端稱為表頭。

image

上圖,很形象的表示了隊列的結構。排在前面的先出,排在后面的后出。換句話,先進的先出,后進額后出。我們在隊尾插入數據,隊頭刪除數據。

 

隊列的抽象數據類型:

同樣是線性表,隊列也有類似線性表的操作,不同的是,插入操作只能在隊尾,刪除操作只能在隊頭。

image

上圖是隊列的抽象數據類型。

順序存儲的隊列:

我們在學習線性表時,知道線性表分為順序存儲與鏈式存儲兩種存儲方式。隊列是特殊的線性表,所以它也分為兩種存儲方式。我們先來看看它的順序存儲結構吧。

隊列順序存儲的不足:

假設有n個元素,我們需要初始化一個長度大於n的數組,來存放這n個元素,下標為0的位置為隊頭。隊列的插入(入隊)操作,是在隊尾進行操作的,隊列中的其他元素不用移動。入隊操作的時間復雜度為O【1】.但是如果,是刪除(出隊)操作,需要在隊頭操作,需要移動隊列中所有元素,以確保我們隊頭(下標為0的位置)不為空。所以,時間復雜度為O【n】。

我們可以改進一下這個隊列,以提高它的效率。我們大可不必,把元素放在數組的前n個位置。也就是說,我們沒必要把下標為0的位置定位隊頭位置。如下圖:

image

為了避免當只有一個元素時,隊頭與隊尾重合,影響我們的操作。所以,我們引入了front、rear指針。front指向第一個元素的位置,rear指向最后一個元素的下一個位置。

這樣,當rear=front時,不是隊列中只有一個元素,而是隊列為空。

這樣我們在進行出隊操作時,隊列中其他元素就不用動了。我們的時間復雜度為o[1].

但是,我們的問題又來了,看下圖:

image

此時,rear已經超出了數組的界限,但是下標為0、1的位置還是空的,這樣是不是挺浪費的?此時,我們的循環隊列就橫空出世了。

循環隊列:隊列的頭尾相接的順序存儲結構稱為循環隊列.

如下圖:

image

這里有一個問題,大家看下圖:

image

此圖中,rear=front,此時隊滿。可是,我們剛才說rear=front時,隊列為空。那么,rear=front時,是空還是滿呢?對於這個問題,我們提供了2中解決方法。

方法一:初始化一個flag變量,當flag=1,rear==front時,隊列滿。當flag=0,rear==front時,隊列空。

方法二:當rear==front時,隊列為空。當rear與front中間僅差一個存儲單元時,隊列為滿。

這里,我們討論一下方法二。看下圖:

image

front與rear之間相處一個存儲單元,此時我們就說隊列已滿。因為rear有時>front,有時<front。我們假設隊列的 最大尺寸為QueueSize,那么我們可以得到計算隊列為滿的公式。

(rear+1)%QueueSize==front.

當rear>front時,rear-front就是隊列的長度。如下圖:

image

當rear<front時,此時的隊列長度分兩部分,一部分為QueueSize-front,另一部分為rear+0。如下圖:

image

將兩部分加在一起,就是隊列的長度。最后,我們得出計算隊列長度的通用公式:

(rear-front+QueueSize)%QueueSize

我們看一下循環隊列的順序存儲結構代碼:

typedef int QElemType

typedef struct

{

    QElemType data[MAXSIZE];

     int front;

    int rear;

}SqQueue;

 

循環隊列的初始化代碼:

Status InitQueue(SqQueue *Q)

{

     Q->front=0;

   Q->rear=0;

return ok;

}

 

循環隊列求隊列長度:

int QueueLength(SqQueue Q)

{

     return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;

}

循環隊列的入隊操作

Status EnQueue(SqQueue *Q,QElemType e)

{

          if((Q->rear+1)==Q->front)/*隊列滿的判斷*/

         return ERROR;

       Q->data[Q->rear]=e;

     Q-rear=(Q->rear+1)%MAXSIZE

return Ok;

}

循環隊列的出隊操作

Status DeQueue(SqQueue *Q,QElemType *e)

{

        if(Q->front=Q->rear)/*隊列空的判斷*/

           return ERROR;

          *e=Q->data[Q->front];

        Q->front=(Q->front+1)%MAXSIZE;

     return ok;

}

從這一段講解,我們發現,單單使用隊列的順序存儲結構,性能是不高的。我們應該使用循環隊列,但是循環隊列又面臨着數組溢出的問題,所以我們還有學習一下不用隊列長度的鏈式存儲結構。

隊列的鏈式存儲:

隊列的鏈式存儲,其實就是線性表的單鏈表。只不過,只能尾進頭出。我們把它簡稱為鏈隊列。為了操作的方便,我們把front指向頭結點,把rear指向終端結點。空隊時,front、rear都指向頭結點。如下圖:
imageimage

鏈隊列的結構:

typedef int QElemType;

/*結點的結構*/

typedef struct QNode

{

         QElemType   data;

         struct   QNode  *next;

}QNode,*QueuePtr;

 

/*鏈表的結構*/

typedef struct

{

         QueuePtr  front,  rear;

}LinkQueue;

 

鏈隊的入隊操作:

Status EnQueue(LinkQueue *Q,QElemType e)

{

           QueuePtr  s=(QueuePtr)malloc(size(QNode));

          if(!s)/*存儲分配失敗*/

          exit(OVERFLOW);

         s->data=e;

       s->next=NULL;

         Q->rear->next=s;

       Q->rear=s;

return ok;

}

鏈隊的出對操作:

Status DeQueue(LinkQueue *Q,QElemType *e)

{

         QueuePtr  P;

       if(Q-front==Q->rear)

return ERROR;

   P=Q->front-next;

*e=p->data;

Q->front->next=p->next;

if(Q->rear==p)

  Q->rear=Q->front;

  free(p);

return ok;

}

我們來比較一下循環隊列與鏈隊的區別:

關於他們的區別,我們從兩方面來分析。時間、空間。

時間:時間復雜度都為O【1】,但是鏈隊在申請、釋放結點時會消耗一些時間。

空間:循環隊列需要固定的長度,會出現存儲元素數量,空間浪費的問題。鏈隊不會出現空間浪費的問題。

總的來說,當我們可以確定長度時,我們選擇循環隊列,否則使用鏈隊。

總結:

這一章,我們主要學習的數據結構是棧(stack)、隊列(Queue).

Stack:只允許在一端進行插入刪除操作。

Queue:只能在一端插入,另一端刪除。

Stack、Queue都是特殊的線性表。所以它們都可以用順序存儲結構來實現,但是都存在一些弊端。它們各自都有解決這些弊端的方法。

Stack,它把相同的數據類型的棧,存放在一個數組中,讓數組一頭為一個棧的棧頂,另一頭為另一個棧的棧頂。最大化的利用了數組空間。

Queue:為了避免出隊,而移動隊元素,於是引入了循環隊列,讓頭尾相連。使得時間復雜度為O【1】.

他們又都可以用鏈式存儲結構實現。

image

這就是這一章的內容了,接下來我們一起學習串。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM