日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
學習隊列,看這一篇就夠了!

提要鉤玄:本文主要介紹隊列的結構、基本原理及操作,涉及到兩種實現(xiàn):順序隊列和鏈隊列。

創(chuàng)新互聯(lián)建站主要從事網(wǎng)站設計、成都網(wǎng)站設計、網(wǎng)頁設計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務。立足成都服務金林,十載網(wǎng)站建設經驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:13518219792

1. 什么是隊列?

先舉一個日常例子,排隊買飯。

排隊買飯

大家按先來后到的順序,在窗口前排隊買飯,先到先得,買完之后走開,輪到下一位買,新來的人排在隊尾,不能插隊。

可見,上面的“隊”的特點是只允許從一端進入,從另一端離開。

這樣的一個隊,放在數(shù)據(jù)結構中就是“隊列”。

首先,隊列是一個線性表,所以它具有線性表的基本特點。

其次,隊列是一個受限的線性表,受限之處為:只允許從一端進入隊列,從另一端離開。

根據(jù)以上特點,可以畫出示意圖:

出隊元素 1,入隊元素 4 之后:

下面是幾個相關名詞:

  • 入隊:進入隊列,即向隊列中插入元素
  • 出隊:離開隊列,即從隊列中刪除元素
  • 隊頭:允許出隊(刪除)的一端
  • 隊尾:允許入隊(插入)的一端
  • 隊頭元素:隊列中最先入棧的元素
  • 隊尾元素:隊列中最后入棧的元素

我們可以直接將隊頭元素看作隊頭,隊尾元素看作隊尾。(這些名詞概念,有所理解即可,不必細究)

隊列的重要特性是在隊尾進行入隊操作,在隊頭進行出隊操作,所以上圖元素的入隊順序為:1、2、3,出隊順序為:1、2、3,也即,先入隊的先出隊(First In First Out, FIFO),后入隊的后出隊(Last In Last Out, LILO).

總結一下,隊列是一種只允許在一端進行插入操作,在另一端進行刪除操作的先入先出的受限的線性表。

2. 隊列的實現(xiàn)思路

和棧一樣,隊列也可以有兩種實現(xiàn)方式:數(shù)組實現(xiàn)的順序隊列和鏈表實現(xiàn)的鏈隊列。

2.1. 數(shù)組實現(xiàn)——順序隊列

一個用數(shù)組實現(xiàn)的順序隊列如下圖所示:

順序隊列

可以看到,要實現(xiàn)一個順序隊列,我們需要以下結構:

  • 存儲數(shù)據(jù)的數(shù)組 —— data[]
  • 表示隊列的最大容量的值 —— MAXSIZE
  • 標識隊頭端的隊頭下標 —— front
  • 標識隊尾端的隊尾下標 —— rear

front 和 rear 會隨著入隊和出隊操作而變化,為了方便起見,我們規(guī)定在非空隊列中,隊尾下標是隊尾元素的下一個元素的下標。

了解了結構之后,我們可以很容易使用 C 語言的結構體實現(xiàn)它:

 
 
 
 
  1. #define MAXSIZE 5 //順序隊列的最大存儲容量
  2. /*順序隊列的結構體*/
  3. typedef struct {
  4.     int data[MAXSIZE];
  5.     int front; //隊頭下標
  6.     int rear; //隊尾下標
  7. } QueueArray;

2.2. 鏈表實現(xiàn)——鏈隊列

我們使用帶頭節(jié)點的單鏈表來實現(xiàn)隊列,如下圖所示:

鏈隊列

可以看到,要實現(xiàn)一個鏈隊列,需要以下結構:

1.單鏈表的基本單元結點 —— QueueNode

  • 存儲數(shù)據(jù)的數(shù)據(jù)域 —— data
  • 指向下一個結點的指針域 —— next

2.指向鏈表的頭指針 —— head

3.標識隊頭端的隊頭指針 —— front

4.標識隊尾端的隊尾指針 —— rear

其中,頭指針 head 和隊頭指針 front 都指向了單鏈表的第一個結點,所以這個指針可以合二為一,隊頭指針即頭指針。

如此一來,我們可以借助鏈表的尾插法實現(xiàn)隊列的入隊操作,借助鏈表的頭刪法實現(xiàn)隊列的出隊操作。

搞清了結構,用結構體實現(xiàn)如下:

 
 
 
 
  1. /*單鏈表的結點的結構體*/
  2. typedef struct QueueNode {
  3.     int data; //數(shù)據(jù)域
  4.     struct QueueNode *next; //指針域
  5. } QueueNode;
  6. /*鏈隊列的結構體*/
  7. typedef struct {
  8.     QueueNode *front; //隊頭指針
  9.     QueueNode *rear; //隊尾指針
  10. } QueueLink;

3. 隊列的狀態(tài)

3.1. 順序隊列(問題版)

【空隊列】:空隊列中沒有元素,此時,隊頭下標和隊尾下標均為 0,即front = rear = 0:

空隊列

【非空非滿隊列】:隊列不是空隊列且有剩余空間:

非空非滿隊列

【滿隊列】:順序隊列分配的固定空間用盡,沒有多余空間,不能再插入元素,此時 front = 0,rear = MAXSIZE:

滿隊列

從上圖中可以看出,非空隊列的隊尾下標 rear 始終是隊尾元素的下一個元素的下標。

3.2. 假滿隊列

以上是用數(shù)組實現(xiàn)的順序隊列的三種狀態(tài),但上圖中三種隊列是存在問題的,那就是隊列的存儲問題!

先再次明確隊列的兩條重要特性:

  • 隊列只允許在隊頭刪除元素,在隊尾插入元素
  • 我們規(guī)定:front 是隊頭元素的下標,rear 是隊尾元素的下標,二者會隨著出隊和入隊操作而變化

由于上面的三幅圖中 front 都在下標 0 處,所以不容易看出問題,請看下面的過程圖:

入隊出隊過程圖

簡單用文字描述以下上述過程:

圖1:空隊列

圖2:進隊 3 個元素:1、2、3

圖3:出隊 2 個元素:1、2

圖4:入隊 2 個元素:4、5

到此為止,一切正常。

圖5:入隊 1 個元素,但在圖4中 rear = 5已經超出數(shù)組的最大范圍,所以圖5入隊一個元素會報錯,這個隊列不能再插入元素了。

圖5的隊列滿了嗎?沒滿!能繼續(xù)插入元素嗎?不能!有剩余空間卻不能用,這就好比有空房的酒店不讓客戶入住,這叫不會做生意。

滿隊列的是空間用盡,不能再插入元素的隊列,雖然圖5的隊列也不能繼續(xù)插入元素了,但它還有剩余空間,所以這樣的隊列還不能稱之為滿隊列,可稱之為假滿隊列。

之所以假滿隊列存在問題,是因為順序隊列的空間是有限的,通過若干入隊操作之后,我們的 rear “跑”到數(shù)組外從而導致越界了。

假滿隊列

明明才存儲了一個元素,卻因為假滿,整個隊列不能再存儲了。這樣的隊列肯定不是合格的數(shù)據(jù)結構。

怎么解決呢?報錯是 rear 越界導致,而隊列的前大部分都是空閑的,所以當 rear 越界時,我們可不可以將其移動到下標 0 處呢?

顯然是可以的,這樣就構成了一個“循環(huán)”,我們稱這種 front 和 rear可以循環(huán)利用的隊列為循環(huán)隊列。

3.3. 循環(huán)隊列

為了突出“循環(huán)”二字,我們將這種順序隊列畫成一個圓:

 循環(huán)隊列

循環(huán)隊列的 rear 和 front 能夠在隊列中一圈一圈地轉,像鐘表的時針和分針一樣。不會再出現(xiàn)不能利用的空間了。

順序隊列的形式從“直的”變成這種可循環(huán)的之后,對于狀態(tài)的判斷也改變了。

【空隊列】:隊列中沒有元素,如上圖。

請注意,空隊列的條件并不是 front = rear = 0,比如一個空隊列經過 3 次入隊和 3 次出隊操作后仍為空隊列:

空隊列

所以,循環(huán)隊列為空隊列時,條件應該為 front = rear

【滿隊列】:隊列中沒有空閑空間

滿隊列

上圖是一個最大容量為 8 的空隊列,入隊 7 個元素后,隊列中還剩 1 個空閑位置,如果此時我們再入隊 1 個元素:

是滿隊列嗎?

此時隊列中確實沒有空閑空間了,但注意,此時隊列滿足了 rear = front ,但滿足 rear = front的隊列不應該是空隊列嗎?

這就產生誤會了。

不如我們退一步海闊天空,少用一個元素,借此來消除誤會。如下圖,規(guī)定這樣是一個滿隊列。

滿隊列

我們規(guī)定,front 出現(xiàn)在 rear 的下一個位置時,隊列為滿隊列。

比如在上圖的滿隊列中, front = 3 在 rear = 2 的下一個位置。

所以隊列為滿隊列的判定條件為:rear + 1 = front,但這的條件是不準確的。

因為循環(huán)隊列中的 front 和 rear 都是循環(huán)使用的,就像鐘表的時針一樣,所以我們僅根據(jù)下標的大小來判斷位置是不合理的。下面兩個均是滿隊列,右圖不滿足rear + 1 = front:

就像鐘表的時針滿 12 歸零一樣,front 和 rear 也應該滿某個數(shù)后歸零,這個數(shù)就是 MAXSIZE。

比如 rear = 7 時,如果按平常做法來 ,下一步應該是 rear = 8,但在這里,我們讓其歸零,所以下一步應該是 rear = 0。

用數(shù)學公式來表示上面的歸零過程就是:rear % MAXSIZE

所以滿隊列的判斷條件應該為:(rear + 1) % MAXSIZE = front。

【非空非滿隊列】:很好理解,不再贅述。

3.4. 鏈隊列

我們使用帶頭結點的單鏈表來實現(xiàn)鏈隊列。

【空隊列】:即一個空鏈表,此時隊頭指針(兼鏈表頭指針)和隊尾指針均指向頭結點。

空隊列

【非空隊列】:不像順序隊列那樣有空間的限制,鏈隊列的空間是不受限制的(只要你的內存足夠大),所以自然不存在“滿隊列”“循環(huán)隊列”的概念。

4. 初始化

在進行隊列的操作前,應該先將其初始化出來,即初始化一個空隊列出來。

4.1. 順序隊列

將隊列的隊頭下標和隊尾下標置為 0 即可。

 
 
 
 
  1. /**
  2.  * 初始化順序隊列:將隊頭下標和隊尾下標置為0
  3.  * queue: 指向隊列的指針
  4.  */
  5. void init(QueueArray *queue)
  6. {
  7.     queue->front = 0;
  8.     queue->rear = 0;
  9. }

4.2. 鏈隊列

創(chuàng)造出頭結點,然后將隊頭指針和隊尾指針均指向頭結點即可。

 
 
 
 
  1. /**
  2.  * 初始化鏈隊列:將隊頭指針和隊尾指針指向頭結點
  3.  */
  4. void init(QueueLink *queue)
  5. {
  6.     //創(chuàng)造頭結點
  7.     QueueNode *head_node = create_node(0);
  8.     //隊頭指針 隊尾指針指向頭結點
  9.     queue->front = head_node;
  10.     queue->rear = head_node;
  11. }

5. 入隊操作

入隊操作只允許元素從隊尾進。

5.1. 順序隊列

前面我們規(guī)定,順序隊列的隊尾下標為隊尾元素的下一個元素,所以直接將待入隊元素放入隊尾下標處,然后隊尾下標“加一”。(注意:循環(huán)隊列中的加一要對 MAXSIZE 取模)

入隊過程

 
 
 
 
  1. /**
  2.  * 入隊操作
  3.  * queue: 指向隊列的指針
  4.  * elem: 入隊的數(shù)據(jù)
  5.  * return: 0失敗,1成功
  6.  */
  7. int en_queue(QueueArray *queue, int elem)
  8. {
  9.     //判斷隊列是否已滿
  10.     if ((queue->rear + 1) % MAXSIZE == queue->front) {
  11.         printf("隊列已滿,無法繼續(xù)入隊。\n");
  12.         return 0;
  13.     }
  14.     //元素入隊
  15.     queue->data[queue->rear] = elem;
  16.     //隊尾下標加一
  17.     queue->rear = (queue->rear + 1) % MAXSIZE;
  18.     return 1;
  19. }

5.2. 鏈隊列

鏈隊列的入隊操作本質是單鏈表的尾插法:

 
 
 
 
  1. /** * 入隊操作
  2.  * queue: 指向隊列的指針
  3.  * elem: 入隊的數(shù)據(jù)
  4.  */
  5. void en_queue(QueueLink *queue, int elem)
  6. {
  7.     //創(chuàng)造新結點
  8.     QueueNode *new = create_node(elem);
  9.     //入隊(尾插法)
  10.     queue->rear->next = new;
  11.     queue->rear = new;
  12. }

6. 出隊操作

出隊操作只允許元素從隊頭出。

6.1. 順序隊列

將隊頭下標處的元素出隊,然后將隊頭下標“加一”(對 MAXSIZE 取模)。

出隊過程

 
 
 
 
  1. /**
  2.  * 出隊操作
  3.  * queue: 指向隊列的指針
  4.  * elem: 指向保存出隊數(shù)據(jù)的變量
  5.  * return: 0失敗,1成功
  6.  */
  7. int de_queue(QueueArray *queue, int *elem)
  8. {
  9.     //判讀隊列是否為空
  10.     if (queue->front == queue->rear) {
  11.         printf("隊列空,無元素可出。\n");
  12.         return 0;
  13.     }
  14.     //元素出隊
  15.     *elem = queue->data[queue->front];
  16.     //隊頭下標加一
  17.     queue->front = (queue->front + 1) % MAXSIZE;
  18.     return 1;
  19. }

6.2. 鏈隊列

鏈隊列的出隊操作本質上是單鏈表的頭刪法。注意,如果出隊的是隊列中最后一個元素,需要在出隊后,將隊尾指針重新指向頭結點,重新形成空隊列。

 
 
 
 
  1. /**
  2.  * 出隊操作
  3.  * queue: 指向隊列的指針
  4.  * elem: 指向保存變量的指針
  5.  * return: 0失敗,1成功
  6.  */
  7. int de_queue(QueueLink *queue, int *elem)
  8. {
  9.     //判讀隊列是否為空
  10.     if (queue->front == queue->rear) {
  11.         printf("隊列空,無元素可出。\n");
  12.         return 0;
  13.     }
  14.     QueueNode *front_node = queue->front->next; //隊頭元素
  15.     //保存數(shù)據(jù)
  16.     *elem = front_node->data;
  17.     //隊頭元素出隊(頭刪法)
  18.     queue->front->next = front_node->next;
  19.     //如果元素出完,隊尾指針重新指向頭結點
  20.     if (front_node == queue->rear)
  21.         queue->rear = queue->front;
  22.     free(front_node);
  23. }

7. 遍歷操作

這里以打印整個隊列為例,介紹如何遍歷隊列。

順序隊列有隊頭下標和隊尾下標,鏈隊列有隊頭指針和隊尾指針,我們要做的就是借助一個臨時變量,從隊頭下標逐個遍歷到隊尾下標即可。

7.1. 順序隊列

借助臨時變量 i,從隊頭下標開始逐個“加一”直到隊尾下標結束。

開始標志為:i = front

加一操作為:i = (i + 1) % MAXSIZE

結束標志為:i % MAXSIZE = rear

 
 
 
 
  1. /**
  2.  * 打印隊列
  3.  */
  4. void output(QueueArray queue)
  5. {
  6.     int i = queue.front;
  7.     while (i % MAXSIZE != queue.rear) {
  8.         printf("%d ", queue.data[i]);
  9.         i = (i + 1) % MAXSIZE;
  10.     }
  11.     printf("\n");
  12. }

如何計算順序隊列的長度?當然你可以遍歷隊列然后借助計數(shù)變量來存儲長度,這樣比較麻煩。因為順序隊列是使用數(shù)組實現(xiàn)的,所以順序隊列的長度我們可以直接根據(jù)下標計算出來。

如果是一個非循環(huán)隊列,那很簡單,直接 rear - front 就是隊列的長度了。

但循環(huán)隊列不能這樣直接減了,因為 rear 和 front 之間的位置關系是不確定的。

左圖 rear < front,我們可以將其長度看成兩部分組成:

  • 下標 0 到 rear,長度為 rear - 0
  • 下標 MAXSIZE - 1 到 rear,長度為 MAXSIZE - front

所以長度為 rear - front + MAXSIZE

為了滿足右圖 rear > front 的情況,如果按照上式,則此時多加了一個 MAXSIZE,所以需要對其再對 MAXIZE 取余。

所以循環(huán)隊列的長度為 (rear - front + MAXSIZE) % MAXSIZE(空隊列也滿足)。

7.2. 鏈隊列

借助指針 p 從隊頭元素遍歷至隊尾元素即可。

 
 
 
 
  1. /**
  2.  * 打印隊列
  3.  */
  4. void output(QueueLink *queue)
  5. {
  6.     QueueNode *p = queue->front->next; //p指向隊頭元素
  7.     while (p != NULL) {
  8.         printf("%d ", p->data);
  9.         p = p->next;
  10.     }
  11.     printf("\n");
  12. }

以上就是隊列的基本原理及操作。


文章題目:學習隊列,看這一篇就夠了!
當前鏈接:http://m.5511xx.com/article/djjipph.html