像棧一樣,隊列(queue)也是一種線性表,它的特性是先進先出,插入在一端,刪除在另一端。就像排隊一樣,剛來的人入隊(push)要排在隊尾(rear),每次出隊(pop)的都是隊首(front)的人。如圖1,描述了一個隊列模型。
隊列(Queue)與棧一樣,是一種線性存儲結構,它具有如下特點:
- 隊列中的數據元素遵循“先進先出”(First In First Out)的原則,簡稱FIFO結構。
- 在隊尾添加元素,在隊頭刪除元素。
1.2 隊列的相關概念
隊列的相關概念:
- 隊頭與隊尾: 允許元素插入的一端稱為隊尾,允許元素刪除的一端稱為隊頭。
- 入隊:隊列的插入操作。
- 出隊:隊列的刪除操作。
例如我們有一個存儲整型元素的隊列,我們依次入隊:{1,2,3}
添加元素時,元素只能從隊尾一端進入隊列,也即是2只能跟在1后面,3只能跟在2后面。
如果隊列中的元素要出隊:
元素只能從隊首出隊列,出隊列的順序為:1、2、3,與入隊時的順序一致,這就是所謂的“先進先出”。
1.3 隊列的操作
隊列通常提供的操作:
- 入隊: 通常命名為push()
- 出隊: 通常命名為pop()
- 求隊列中元素個數
- 判斷隊列是否為空
- 獲取隊首元素
1.4 隊列的存儲結構
隊列與棧一樣是一種線性結構,因此以常見的線性表如數組、鏈表作為底層的數據結構。
本文中,我們以數組、鏈表為底層數據結構構建隊列。
2.基於數組的循環隊列實現
以數組作為底層數據結構時,一般講隊列實現為循環隊列。這是因為隊列在順序存儲上的不足:每次從數組頭部刪除元素(出隊)后,需要將頭部以后的所有元素往前移動一個位置,這是一個時間復雜度為O(n)的操作:
可能有人說,把隊首標志往后移動不就不用移動元素了嗎?的確,但那樣會造成數組空間的“流失”。
我們希望隊列的插入與刪除操作都是O(1)的時間復雜度,同時不會造成數組空間的浪費,我們應該使用循環隊列。
所謂的循環隊列,可以把數組看出一個首尾相連的圓環,刪除元素時將隊首標志往后移動,添加元素時若數組尾部已經沒有空間,則考慮數組頭部的空間是否空閑,如果是,則在數組頭部進行插入。
C語言實現:數組實現的隊列,並且只能存儲int數據
C語言數組實現隊列一(數組模擬):
#include <stdio.h> /** * C 語言: 數組實現的隊列,只能存儲int數據。 * * @author llz * @date 2018/7/02 */ // 保存數據的數組 int arr[500]; // 隊列的實際大小 int count; // 將val添加到隊列的末尾 void add(int val) { arr[count++] = val; } // 返回“隊列開頭元素” int front() { return arr[0]; } // 返回並刪除“隊列開頭元素” int pop() { int i = 0;; int ret = arr[0]; count--; while (i++<count) arr[i-1] = arr[i]; return ret; } // 返回“隊列”的大小 int size() { return count; } // 返回“隊列”是否為空 int is_empty() { return count==0; } int main() { int tmp=0,i; // 打印隊列 for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 將10, 20, 30 依次推入隊列中 add(10); add(20); add(30); for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 將“隊列開頭的元素”賦值給tmp,並刪除“該元素” tmp = pop(); printf("tmp=%d\n", tmp); // 只將“隊列開頭的元素”賦值給tmp,不刪除該元素. tmp = front(); printf("tmp=%d\n", tmp); add(40); // 打印隊列 printf("is_empty()=%d\n", is_empty()); printf("size()=%d\n", size()); while (!is_empty()) { printf("%d\n", pop()); } return 0; }
結果說明:該示例中的隊列,是通過"數組"來實現的!
由於代碼中已經給出了詳細了注釋,這里就不再對函數進行說明了。僅對主函數main的邏輯進行簡單介紹。
(01) 在主函數main中,先將 "10, 20, 30"依次入隊列。此時,隊列的數據是: 10 --> 20 --> 30
(02) 接着通過pop()返回隊首元素;pop()操作並不會改變隊列中的數據。此時,隊列的數據依然是: 10 --> 20 --> 30
(03) 接着通過front()返回並刪除隊首元素。front()操作之后,隊列的數據是: 10 --> 30
(04) 接着通過add(40)將40入隊列。add(40)操作之后,隊列中的數據是: 10 --> 20 --> 40
C語言數組實現隊列二(數組模擬+隊頭隊尾指針+循環隊列):
#include <stdio.h> /** * C 語言: 數組實現的隊列,只能存儲int數據。 * * @author llz * @date 2018/7/02 */ // 保存數據的數組 int arr[50]; // 隊列的實際大小 int count; //隊頭和隊尾下標 int front; int tail; int size() { return tail-front; } // 返回“隊列”是否為空 int is_empty() { if(front==tail) return 1; return 0; } // 返回“隊列”是否為滿 int is_full() { if(front==(tail+1)%50) return 1; return 0; } // 將val添加到隊列的末尾 void add(int val) { if(!is_full()) { arr[tail] = val; tail=(tail+1)%50; } else { printf("隊列滿\n"); } } // 返回“隊列開頭元素” int first() { return arr[front]; } // 返回並刪除“隊列開頭元素” int pop() { int ret; if(!is_empty()) { ret=arr[front] ; front=(front+1)%50; } else { printf("隊列空\n"); } return ret; } int main() { int tmp=0,i; //輸出隊列中的每個元素 for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 將10, 20, 30 依次推入隊列中 add(10); add(20); add(30); for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 將“隊列開頭的元素”賦值給tmp,並刪除“該元素” tmp = pop(); printf("tmp=%d\n", tmp); // 只將“隊列開頭的元素”賦值給tmp,不刪除該元素. tmp = first(); printf("tmp=%d\n", tmp); add(40); // 打印隊列 printf("is_empty()=%d\n", is_empty()); //printf("size()=%d\n", size()); while (!is_empty()) { printf("%d\n", pop()); } return 0; }