ref : https://blog.csdn.net/qq_29542611/article/details/78907339
隊列的鏈式存儲結構,其實就是線性表的單鏈表,只不過它只是尾進頭出而已,我們把它簡稱為鏈隊列。為了操作上的方便,我們將隊頭指針指向鏈隊列的頭結點,而隊尾指針指向終端節點。如果
空隊列時,front和rear都指向頭結點。
入隊操作:
在隊尾添加元素,先將隊尾元素的next指向添加的元素,然后將隊尾指針重新指向新的隊尾即可。
出隊操作:
頭結結點指向的結點即為隊頭結點,出隊操作,就是把隊頭結點干掉,先把頭結點指向新的隊頭結點(也就是舊的隊頭結點的后繼結點),然后釋放舊的隊頭結點。如果鏈表除頭結點外只剩一個元素時,則需將rear指向頭結點即可。
下面是隊列鏈式存儲結構實現的具體代碼:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #define QUEUESIZE 10 #define ERROR 0 #define OK 1 #define TRUE 1 #define FALSE 0 #define EleType int #define Status int //鏈隊列結點 typedef struct QueueNode { EleType e;//數據域 struct QueueNode* next;//指針域 }QueueNode,*LinkQueuePoi; typedef struct LinkQueue { LinkQueuePoi front;//指向頭結點 LinkQueuePoi rear;//指向隊尾 }LinkQueue; /* 初始化鏈隊列 鏈隊列為空時,鏈隊列隊頭指針隊尾指針均指向頭結點 */ Status InitLinkQueue(LinkQueue* queue) { //空指針 if (!queue) { return ERROR; } QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));//頭結點 node->next = NULL; queue->front = queue->rear = node; return OK; } /* 清空鏈隊列 將所有元素釋放 */ Status CleaerLinkQueue(LinkQueue* queue) { //空指針 if (!queue) { return ERROR; } //空鏈隊列 if (queue->front == queue->rear) { return ERROR; } QueueNode* node = queue->front->next;//隊頭元素 while (node) { queue->front->next = node->next;//指向新的隊頭結點 if (queue->rear == node)//當刪除的是隊尾元素時,將隊尾指針指向頭結點 { queue->rear = queue->front; } free(node);//釋放舊的隊頭結點 node = queue->front->next; } return OK; } /* 判斷鏈隊列是否為空隊列 */ Status EmptyLinkQueue(LinkQueue* queue) { //空指針 if (!queue) { return ERROR; } //空鏈隊列 if (queue->front == queue->rear) { return TRUE; } return FALSE; } /* 獲取鏈隊列長度 */ int LengthLinkQueue(LinkQueue* queue) { //空指針 if (!queue) { return ERROR; } //空鏈隊列 if (queue->front == queue->rear) { return 0; } QueueNode* node = queue->front->next; int num = 0; while (node) { node = node->next; num++; } return num; } /* 在鏈隊列隊尾添加元素 先將新元素添加到鏈表尾部,然后將隊列尾指針指向這個新元素 */ Status AddQueue(LinkQueue* queue,EleType e) { //空指針 if (!queue) { return ERROR; } QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode)); if (!node) { return ERROR; } node->next = NULL; node->e = e; queue->rear->next = node;//將新結點添加到鏈表表中 queue->rear = node;//隊尾指針指向新的隊尾結點 return OK; } /* 從鏈隊列中刪除隊頭元素 先將頭結結點指向新的隊頭結點,然后釋放原來的隊頭結點 */ Status DelQueue(LinkQueue* queue, EleType *e) { //空指針 if (!queue) { return ERROR; } //注意queue->front是頭結點,頭結點指向的結點才是隊頭結點 QueueNode* node = queue->front->next;//舊隊頭結點 *e = node->e; queue->front->next = node->next;//隊頭指針指向新的隊頭結點 //當刪除的是隊尾元素時,將隊尾指針指向頭結點 if (node = queue->rear) { queue->rear = queue->front; } return OK; } /* 打印鏈隊列元素 */ void PrintfLinkQueue(LinkQueue* queue) { if (!queue) { return; } QueueNode* node = queue->front->next; while (node) { printf("%d,", node->e); node = node->next; } printf("\n"); return; } int main(int argc, char *argv[]) { LinkQueue queue; InitLinkQueue(&queue); AddQueue(&queue, 1); AddQueue(&queue, 2); AddQueue(&queue, 3); AddQueue(&queue, 4); AddQueue(&queue, 5); AddQueue(&queue, 6); AddQueue(&queue, 7); AddQueue(&queue, 8); AddQueue(&queue, 9); printf("鏈隊列元素個數:%d\n",LengthLinkQueue(&queue)); printf("展示元素:\n"); PrintfLinkQueue(&queue); int e1, e2; DelQueue(&queue, &e1); DelQueue(&queue, &e2); printf("刪除元素:%d,%d\n", e1, e2); printf("展示元素:\n"); PrintfLinkQueue(&queue); printf("鏈隊列元素個數:%d\n", LengthLinkQueue(&queue)); CleaerLinkQueue(&queue); printf("清空元素后,長度為%d,rear = %p,front=%p",LengthLinkQueue(&queue), queue.rear,queue.front); printf("\n"); return 0; }
驗證結果截圖:
對於循環隊列與鏈隊列的比較,可以從時間和空間2方面來考慮,從時間上,他們的基本操作都是常數時間,即都為O(1),不過循環隊列是事先申請好空間,使用期間不釋放,而對於鏈隊列,每次申請和釋放結點也會存在一些時間開銷,如果入隊出隊頻繁,則2者還是有些細微的差異。對於空間方面來說,循環隊列必須有一個固定的長度,所以就有了存儲元素個數和空間浪費的問題。而鏈隊列就不存在這個問題,盡管它需要一些指針域,會產生一些空間上的開銷,但也可以接受。所以在空間上,鏈隊列更加靈活。