数据结构——队列(queue)
隊列(queue)
隊列(queue):只允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
隊列是一種先進先出(First In First Out)的線性表,簡稱FIFO。
允許插入的一端稱為隊尾,允許刪除的一端稱為隊頭
1、抽象數據類型
ADT 隊列(Queue) Data同線性表。元素具有相同的類型,相鄰元素具有前驅和后繼關系。 OperationInitQueue(*Q): 初始化操作,建立一個空隊列Q。DestroyQueue(*Q): 若隊列Q存在,則銷毀它。ClearQueue(*Q): 將隊列 Q 清空。QueueEmpty(Q): 若隊列Q為空,送回true,否則退回false。GetHead(Q, *e): 若隊列Q存在且非空,用e返因隊列Q的隊頭元素。EnQueue(*Q,e): 若隊列Q存在,插入新元素e到隊列Q中并成為隊尾元素。DeQueue(*Q, *e): 刪除隊列Q中隊頭元素,并用e返回其值。QueueLength(Q): 送回隊列Q的元素個教。 endADT2、循環隊列(順序隊列)
下圖為隊列的假溢出示意圖,即下標 0 和 1 的位置還是空閑的,然而指向隊尾的指針rear已經出現了數組越界的錯誤。為了避免這種情況,一般順序隊列也指循環隊列。
循環隊列:采用環狀順序表來存放隊列元素,并用兩個指針,其中 front 指針指向隊列的隊頭元素,rear指針指向隊尾元素的下一個位置,往隊列中加進或取出元素時分別改變這兩個變量的計數。當頭尾指針(front / rear)指向隊列尾的元素(下標:QueueSize-1)時,其加1操作的結果是指向向量的下界0。
2.1 特殊情況的判定——空\滿隊列
??當隊列為空或者滿的時候都會出現 front == rear 的情況,即無法通過條件 front == rear 來判別隊列是”空”還是”滿”。
解決這個問題的方法至少有三種:
① 另設一布爾變量或標志變量以區別隊列的空和滿;
② 保留一個元素空間。約定入隊前,測試對尾指針 rear 在循環意義下加1后是否等于 front,若相等則認為隊滿(注意:rear所指的單元始終為空),此時判空條件仍是 front == rear,判滿條件改變;
③使用一個計數器記錄隊列中元素的總數(即隊列長度)。
下文針對②進行討論。
2.2 實現
隊列空的條件:
???? ? ? ???? ? ? front == rear
隊列滿的條件:
???? ? ? (rear + 1)% QueueSize == front
??其中,rear為指向隊尾元素的下一位置的指針,front 為指向隊頭元素的指針。QueueSize為隊列的最大長度。
通用計算隊列長度公式:
???? (rear - front + QueueSize) % QueueSize
代碼實現
注意:傳參的形式傳址還是傳實體對象,實體對象需要用實心點來指向成員,所以判斷條件寫成 Q.front==Q.rear;傳址則為Q->front == Q->rear
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存儲空間初始分配量 */typedef int Status; typedef int QElemType; /* QElemType類型根據實際情況而定,這里假設為int *//* 循環隊列的順序存儲結構 */ typedef struct {QElemType data[MAXSIZE];int front; /* 頭指針 */int rear; /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */ }SqQueue;Status visit(QElemType c) {printf("%d ",c);return OK; }/* 初始化一個空隊列Q */ Status InitQueue(SqQueue *Q) {Q->front=0;Q->rear=0;return OK; }/* 將Q清為空隊列 */ Status ClearQueue(SqQueue *Q) {Q->front=Q->rear=0;return OK; }/* 若隊列Q為空隊列,則返回TRUE,否則返回FALSE */ Status QueueEmpty(SqQueue Q) { if(Q.front==Q.rear) /* 隊列空的標志 */return TRUE;elsereturn FALSE; }/* 返回Q的元素個數,也就是隊列的當前長度 */ int QueueLength(SqQueue Q) {return (Q.rear-Q.front+MAXSIZE)%MAXSIZE; }/* 若隊列不空,則用e返回Q的隊頭元素,并返回OK,否則返回ERROR */ Status GetHead(SqQueue Q,QElemType *e) {if(Q.front==Q.rear) /* 隊列空 */return ERROR;*e=Q.data[Q.front];return OK; }/* 若隊列未滿,則插入元素e為Q新的隊尾元素 */ Status EnQueue(SqQueue *Q,QElemType e) {if ((Q->rear+1)%MAXSIZE == Q->front) /* 隊列滿的判斷 */return ERROR;Q->data[Q->rear]=e; /* 將元素e賦值給隊尾 */Q->rear=(Q->rear+1)%MAXSIZE;/* rear指針向后移一位置, *//* 若到最后則轉到數組頭部 */return OK; }/* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */ Status DeQueue(SqQueue *Q,QElemType *e) {if (Q->front == Q->rear) /* 隊列空的判斷 */return ERROR;*e=Q->data[Q->front]; /* 將隊頭元素賦值給e */Q->front=(Q->front+1)%MAXSIZE; /* front指針向后移一位置, *//* 若到最后則轉到數組頭部 */return OK; }/* 從隊頭到隊尾依次對隊列Q中每個元素輸出 */ Status QueueTraverse(SqQueue Q) { int i;i=Q.front;while((i+Q.front)!=Q.rear){visit(Q.data[i]);i=(i+1)%MAXSIZE;}printf("\n");return OK; }int main() {Status j;int i=0,l;QElemType d;SqQueue Q;InitQueue(&Q);printf("初始化隊列后,隊列空否?%u(1:空 0:否)\n",QueueEmpty(Q));printf("請輸入整型隊列元素(不超過%d個),-1為提前結束符: ",MAXSIZE-1);do{/* scanf("%d",&d); */d=i+100;if(d==-1)break;i++;EnQueue(&Q,d);}while(i<MAXSIZE-1);printf("隊列長度為: %d\n",QueueLength(Q));printf("現在隊列空否?%u(1:空 0:否)\n",QueueEmpty(Q));printf("連續%d次由隊頭刪除元素,隊尾插入元素:\n",MAXSIZE);for(l=1;l<=MAXSIZE;l++){DeQueue(&Q,&d);printf("刪除的元素是%d,插入的元素:%d \n",d,l+1000);/* scanf("%d",&d); */d=l+1000;EnQueue(&Q,d);}l=QueueLength(Q);printf("現在隊列中的元素為: \n");QueueTraverse(Q);printf("共向隊尾插入了%d個元素\n",i+MAXSIZE);if(l-2>0)printf("現在由隊頭刪除%d個元素:\n",l-2);while(QueueLength(Q)>2){DeQueue(&Q,&d);printf("刪除的元素值為%d\n",d);}j=GetHead(Q,&d);if(j)printf("現在隊頭元素為: %d\n",d);ClearQueue(&Q);printf("清空隊列后, 隊列空否?%u(1:空 0:否)\n",QueueEmpty(Q));return 0; }3、隊列的鏈式存儲結構(鏈隊列)及實現
3.1 鏈隊列定義
隊頭指針( front )指向鏈隊列的頭結點,而隊尾指針( rear )指向終端結點。非空鏈隊列如下所示。
當隊列為空時,front和rear都指向頭結點。
結構如下:
#include "stdio.h"/* QElemType類型根據實際情況而定,這里假設為int */ typedef int QElemType;typedef struct QNode /* 結點結構 */ {QElemType data;struct QNode *next; } QNode,*QueuePtr;typedef struct /* 隊列的鏈表結構 */ {QueuePtr front,rear; /* 隊頭、隊尾指針 */ } LinkQueue;int main() {return 0; }3.2 入隊操作
步驟:
1、根據圖,我們先創建一個結點s,QueuePtr s=(QueuePtr)malloc(sizeof(QNode));
2、然后給s的data域賦值e,指針域next賦值null。s->data=e;s->next=NULL; 目的就是讓它成為新任隊尾元素。
3、前任隊尾元素呢?直接讓它的指針域指向s就行了。Q->rear->next=s;
4、別忘了把隊尾指針重新指向新任隊尾s。Q->rear=s;
3.3 出隊操作
一般情況:
如果鏈隊列只剩下一個元素的時候,出隊則如下圖:
3.4 實現
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存儲空間初始分配量 */typedef int Status; typedef int QElemType; /* QElemType類型根據實際情況而定,這里假設為int */typedef struct QNode /* 結點結構 */ {QElemType data;struct QNode *next; }QNode,*QueuePtr;typedef struct /* 隊列的鏈表結構 */ {QueuePtr front,rear; /* 隊頭、隊尾指針 */ }LinkQueue;Status visit(QElemType c) {printf("%d ",c);return OK; }/* 構造一個空隊列Q */ Status InitQueue(LinkQueue *Q) { Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));if(!Q->front)exit(OVERFLOW);Q->front->next=NULL;return OK; }/* 銷毀隊列Q */ Status DestroyQueue(LinkQueue *Q) {while(Q->front){Q->rear=Q->front->next;free(Q->front);Q->front=Q->rear;}return OK; }/* 將Q清為空隊列 */ Status ClearQueue(LinkQueue *Q) {QueuePtr p,q;Q->rear=Q->front;p=Q->front->next;Q->front->next=NULL;while(p){q=p;p=p->next;free(q);}return OK; }/* 若Q為空隊列,則返回TRUE,否則返回FALSE */ Status QueueEmpty(LinkQueue Q) { if(Q.front==Q.rear)return TRUE;elsereturn FALSE; }/* 求隊列的長度 */ int QueueLength(LinkQueue Q) { int i=0;QueuePtr p;p=Q.front;while(Q.rear!=p){i++;p=p->next;}return i; }/* 若隊列不空,則用e返回Q的隊頭元素,并返回OK,否則返回ERROR */ Status GetHead(LinkQueue Q,QElemType *e) { QueuePtr p;if(Q.front==Q.rear)return ERROR;p=Q.front->next;*e=p->data;return OK; }/* 插入元素e為Q的新的隊尾元素 */ Status EnQueue(LinkQueue *Q,QElemType e) { QueuePtr s=(QueuePtr)malloc(sizeof(QNode));if(!s) /* 存儲分配失敗 */exit(OVERFLOW);s->data=e;s->next=NULL;Q->rear->next=s; /* 把擁有元素e的新結點s賦值給原隊尾結點的后繼,見圖中① */Q->rear=s; /* 把當前的s設置為隊尾結點,rear指向s,見圖中② */return OK; }/* 若隊列不空,刪除Q的隊頭元素,用e返回其值,并返回OK,否則返回ERROR */ Status DeQueue(LinkQueue *Q,QElemType *e) {QueuePtr p;if(Q->front==Q->rear)return ERROR;p=Q->front->next; /* 將欲刪除的隊頭結點暫存給p,見圖中① */*e=p->data; /* 將欲刪除的隊頭結點的值賦值給e */Q->front->next=p->next;/* 將原隊頭結點的后繼p->next賦值給頭結點后繼,見圖中② */if(Q->rear==p) /* 若隊頭就是隊尾,則刪除后將rear指向頭結點,見圖中③ */Q->rear=Q->front;free(p);return OK; }/* 從隊頭到隊尾依次對隊列Q中每個元素輸出 */ Status QueueTraverse(LinkQueue Q) {QueuePtr p;p=Q.front->next;while(p){visit(p->data);p=p->next;}printf("\n");return OK; }int main() {int i;QElemType d;LinkQueue q;i=InitQueue(&q);if(i)printf("成功地構造了一個空隊列!\n");printf("是否空隊列?%d(1:空 0:否) ",QueueEmpty(q));printf("隊列的長度為%d\n",QueueLength(q));EnQueue(&q,-5);EnQueue(&q,5);EnQueue(&q,10);printf("插入3個元素(-5,5,10)后,隊列的長度為%d\n",QueueLength(q));printf("是否空隊列?%d(1:空 0:否) ",QueueEmpty(q));printf("隊列的元素依次為:");QueueTraverse(q);i=GetHead(q,&d);if(i==OK)printf("隊頭元素是:%d\n",d);DeQueue(&q,&d);printf("刪除了隊頭元素%d\n",d);i=GetHead(q,&d);if(i==OK)printf("新的隊頭元素是:%d\n",d);ClearQueue(&q);printf("清空隊列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);DestroyQueue(&q);printf("銷毀隊列后,q.front=%u q.rear=%u\n",q.front, q.rear);return 0; }總結
以上是生活随笔為你收集整理的数据结构——队列(queue)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PVANET: Deep but Lig
- 下一篇: 数据结构——字符串(未完)