隊列定義
隊列的工作原理和現實中的隊列完全一致,比如排隊上車,排前面的先上車,排後面的後上車。隊列只支持兩種操作,入隊和出隊,是一種先進先出的數據結構(First In First Out,FIFO),順序和鏈式兩種結構的隊列如下:
隊列基本操作
- 初始化隊列 initQueue():初始化隊列空間及隊首隊尾指針 head、tail
- 判斷隊列是否爲空 isEmpty():當隊首、隊尾相等時,隊爲空,即 head == tail 是返回真,否返回假
- 入隊 enQueue():將元素從隊尾 tail 處加入隊列,並且更新隊尾指針,即 tail++
- 出隊 delQueue():從隊首元素開始出隊,並且更新隊首指針,即 head++
- 讀隊首元素 getHeadValue():讀取隊首元素的值
順序隊列實現
#include <stdio.h>
#include <string.h>
#define true 1
#define false 0
#define MAX_SIZE 100
typedef struct {
char data[MAX_SIZE];
int head;
int tail;
}Queue;
void initQueue(Queue *queue){
memset(queue -> data, 0, MAX_SIZE);
queue -> head = 0;
queue -> tail = 0;
printf("######## 隊列初始化完成! ########\n");
}
int isEmpty(Queue *queue){
if(queue -> head == queue -> tail) {
printf("完成出隊! Queue -> head = Queue -> tail = %d\n", queue -> head);
return true;
}
return false;
}
char getHeadValue(Queue *queue){
return queue -> data[queue -> head];
}
void enQueue(Queue *queue, char value){
queue -> data[queue -> tail] = value;
queue -> tail++;
printf("元素 %c 入隊!\n",queue -> data[queue -> tail - 1]);
}
void delQueue(Queue *queue){
printf("元素 %c 將出隊!\n",getHeadValue(queue));
queue -> data[queue -> head] = 0;
queue -> head++;
}
int main()
{
Queue Q;
initQueue(&Q);
char str[MAX_SIZE] = {0};
printf("請輸入數據,按Enter結束!\n");
scanf("%s", str);
int i;
printf("######### 開始入隊 #########\n");
for(i = 0; i < strlen(str); i++) {
enQueue(&Q, str[i]);
}
printf("######### 開始出隊 #########\n");
while(1) {
if(isEmpty(&Q)) {
break;
}
delQueue(&Q);
}
return 0;
}
該程序模擬隊列基本操作,執行過程如下:
鏈式隊列實現
鏈式隊列通過鏈表來存儲數據,因此需要建立一個鏈表結構(關於鏈表看這裏),在此基礎上再建立隊列結構,定義如下:
typedef struct ListNode{
char data;
struct ListNode *next;
}Lnode;
typedef struct ListQueue{
Lnode *head;
Lnode *tail;
}Lqueue;
初始化鏈式隊列
鏈式隊列頭尾指針指向同一個空節點
void Init_ListQueue(Lqueue *queue) {
queue -> head = queue -> tail = (Lnode *)malloc(sizeof(Lnode));
if(queue -> head == NULL) {
printf("Malloc Failed!\n");
exit(0);
}
queue -> head -> next = NULL;
}
元素入隊
由隊尾添加元素,隊尾指針始終指向新增節點(node)的地址,移動 tail 指針
void Push_ListValue(Lqueue *queue, char value) {
Lnode *node = (Lnode *)malloc(sizeof(Lnode));
node -> data = value;
node -> next = NULL;
queue -> tail -> next = node;
queue -> tail = queue -> tail -> next;
printf("Push: %c\n", queue -> tail -> data);
}
元素出隊
移動隊頭指針出隊,head -> next 指向第一個節點地址,當 head -> next 等於隊尾指針時,已到鏈隊末尾,隊尾指針出隊,出隊結束,釋放 head -> next 空間,鏈隊空!
void Pop_ListValue(Lqueue *queue) {
if(queue -> head -> next == queue -> tail) {
printf("Pop: %c\n", queue -> tail -> data);
free(queue -> head -> next);
queue -> head -> next = NULL;
printf("隊列已空!\n");
return;
}
else {
Lnode *tmp = queue -> head -> next;
printf("Pop: %c\n", tmp -> data);
queue -> head -> next = tmp -> next;
free(tmp);
tmp = NULL;
}
}
測試代碼
當隊列爲空時,需要釋放頭節點指針 LQ.head. 此處多一次Pop操作來驗證隊列是否已釋放空
void test_1() {
Lqueue LQ;
Init_ListQueue(&LQ);
Push_ListValue(&LQ, 'A');
Push_ListValue(&LQ, 'B');
Push_ListValue(&LQ, 'C');
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
free(LQ.head);
}
執行結果打印如下:
Push: A
Push: B
Push: C
Pop: A
Pop: B
Pop: C
隊列已空!
循環隊列
循環隊列最多存儲(MAXSIZE - 1)個隊列元素,因此當循環隊列中只剩下一個空存儲單元時,說明隊列已經滿了,即隊列判滿條件是 head = (tail + 1) % MAXSIZE;隊列判空的條件是 head == tail;
在實際開發中,比較常見的隊列是循環雙端隊列,就是隊列兩端都可以進出的隊列,如下圖:
對於循環雙端隊列的實現,Leetcode上面有一道題目,小編之前有總結一篇博客,這裏貼出鏈接C語言實現循環雙端隊列
好了,關於隊列的學習總結就到這了,之後有新的感悟再做更新…