佇列的鏈式儲存結構及實現
阿新 • • 發佈:2019-01-05
佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只是尾進頭出而已,我們把它簡稱為鏈佇列。為了操作上的方便,我們將隊頭指標指向鏈佇列的頭結點,而隊尾指標指向終端節點。如果
空佇列時,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者還是有些細微的差異。對於空間方面來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間浪費的問題。而鏈佇列就不存在這個問題,儘管它需要一些指標域,會產生一些空間上的開銷,但也可以接受。所以在空間上,鏈佇列更加靈活。