1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define OK 1 5 #define ERR 2 6 #define TRUE 1 7 #define FALSE 0 8 #define MAXSIZE 4 //定義隊列的最大長度 9 10 typedef int status; //定義函數返回的狀態,OK & ERR 11 typedef char datatype; //定義隊列中每個元素的數據類型,這里暫定為字符型 12 13 typedef struct { 14 datatype data[MAXSIZE]; //存儲着隊列中的每個元素 15 int front, rear; //頭指針和尾指針 16 /* 17 假設用於模擬隊列的數組長度為8,規定入隊和出隊方向都向左(即下標0的元素始終用於出隊),有一個指針end永遠標識隊尾元素的下標,當end=0時表示隊列只有一個元素,當end=-1時標識隊列為空,類似於棧的top指針, 18 現在入隊3個元素,數組如下, 19 A B C _ _ _ _ _ 指針end=2 20 出隊1個元素,即下標是0的元素出隊,也就是A, 21 _ B C _ _ _ _ _ 指針end=2 22 這時下標0的元素為空,需要將后面的元素都前移,然后將指針end-1,如果隊列很長,這個操作會帶來很大開銷,所以我們不應該用棧的固定思維去思考隊列,也就是說隊頭不一定要在下標為0的位置, 23 這樣一來我們定義一個指針front用來表示隊頭所在元素的下標,再定義一個指針rear用來表示隊尾所在元素的下一個元素的下標,當front=rear時表示隊列為空,這就是隊列的初始狀態, 24 現在我們用上述隊列的存儲結構來創建一個新隊列, 25 _ _ _ _ _ _ _ _ 指針front=0 指針rear=0 26 入隊3個元素, 27 A B C _ _ _ _ _ 指針front=0 指針rear=3 28 出隊1個元素, 29 _ B C _ _ _ _ _ 指針front=1 指針rear=3 30 再入隊3個元素, 31 _ B C D E F _ _ 指針front=1 指針rear=6 32 再出隊3個元素, 33 _ _ _ _ E F _ _ 指針front=4 指針rear=6 34 可見新定義的隊列的存儲結構不需要大量前移元素了(因為隊頭元素由指針front唯一確定),這樣,入隊只需要rear+1,出隊只需要front+1, 35 但是又出現了個很嚴重的問題:當繼續入隊2個(G和H)元素,H元素便成為數組的最后一個元素,此時rear=8,而下標為8的位置是越界的,而數組最前面的下標(0~3,共4個)卻空着,就造成了浪費,這種現象叫做假溢出, 36 要解決上述問題,可將隊列的頭和尾相連,使之成為循環隊列, 37 當數組的最后一個元素也被使用了,此時可將rear等於0而不是8, 38 _ _ _ _ E F G H 指針front=4 指針rear=0 39 這時,再入隊3個元素,數組如下, 40 I J K _ E F G H 指針front=4 指針rear=3 41 我們將循環隊列的這種情況稱為隊列已滿(rear和front之間空一個元素),不然再入隊一個元素(L),那么rear=4,和front相等了,這時是空隊列呢還是滿隊列呢? 42 循環隊列已滿的條件:(rear+1)%QueueSize == front 43 循環隊列長度公式:(rear-front+QueueSize)%QueueSize 44 入隊(rear后移一位):rear=(rear+1)%QueueSize 45 出隊(front后移一位):front=(front+1)%QueueSize 46 其中QueueSize是隊列的最大長度(數組的長度),比如上面演示的隊列的QueueSize就是8 47 循環隊列長度公式的由來,(以下討論的數都是整數) 48 當rear>front時,隊列的長度:rear-front,其中rear-front必然是正數 49 當rear<front時,隊列的長度由兩部分組成, 50 第一部分是(空元素的后面):QueueSize-front 51 第二部分是(空元素的前面):0+rear 52 綜上,隊列的長度:rear-front+QueueSize,其中rear-front必然是負數 53 如果rear-front+QueueSize這個公式用於rear>front的隊列,那么得到的隊列長度就大於數組的最大長度(QueueSize), 54 那到底大了多少呢,其實就是大了QueueSize,但我們又不能減去QueueSize,這樣就不能計算rear<front的隊列長度, 55 正確解決方法是對rear-front+QueueSize取余,模數是QueueSize,即(rear-front+QueueSize)%QueueSize, 56 這樣, 57 對於rear>front,雖然先加上了QueueSize,但最后的結果模上了QueueSize,相當於抵消了之前加的QueueSize 58 對於rear<front,由於rear-front必然是負數,但這個負數是大於-QueueSize的(這個負數的最小值是-QueueSize+1),所以rear-front+QueueSize的范圍是[1,QueueSize-1],對這個區間里的任何一個數模上QueueSize還是這個數本身 59 其實對rear-front+QueueSize模上QueueSize是為了兼容rear>front的隊列 60 61 */ 62 } SequenceQueue; 63 64 /* 函數原型,隊列的基本操作,與棧相同 */ 65 SequenceQueue *createSequenceQueue(void); 66 status isEmpty(SequenceQueue *L); 67 void clear(SequenceQueue *L); 68 datatype getTop(SequenceQueue *L); 69 int getLength(SequenceQueue *L); 70 status push(SequenceQueue *L, datatype node_to_push); 71 datatype pop(SequenceQueue *L); 72 void showQueue(SequenceQueue *L); 73 74 int main(){ 75 /* 測試 */ 76 SequenceQueue *root; //指向一個通過createSequenceQueue函數創建的棧 77 root=createSequenceQueue(); 78 printf("isEmpty = %d\n",isEmpty(root)); 79 printf("Length = %d\n",getLength(root)); 80 push(root,'1'); 81 push(root,'2'); 82 push(root,'3'); 83 printf("isEmpty = %d\n",isEmpty(root)); 84 printf("Length = %d\n",getLength(root)); 85 showQueue(root); 86 putchar('\n'); 87 printf("can continue to push? %d\n",push(root,'4')); 88 printf("getTop = %c\n",getTop(root)); 89 printf("pop = %c\n",pop(root)); 90 printf("pop = %c\n",pop(root)); 91 printf("isEmpty = %d\n",isEmpty(root)); 92 printf("Length = %d\n",getLength(root)); 93 push(root,'5'); 94 showQueue(root); 95 putchar('\n'); 96 clear(root); 97 printf("isEmpty = %d\n",isEmpty(root)); 98 printf("Length = %d\n",getLength(root)); 99 100 return 0; 101 } 102 103 SequenceQueue *createSequenceQueue(void){ 104 SequenceQueue *tmp; 105 tmp=malloc(sizeof(SequenceQueue)); //void*類型指針能自動轉為其他類型的指針 106 tmp->front=tmp->rear=0; //初始化隊列的頭尾指針 107 return tmp; 108 } 109 status isEmpty(SequenceQueue *L){ 110 if (L->front==L->rear) //front=rear表示隊列是空的 111 return TRUE; 112 else 113 return FALSE; 114 } 115 void clear(SequenceQueue *L){ 116 L->front=L->rear=0; 117 } 118 datatype getTop(SequenceQueue *L){ 119 //返回隊頭元素的值 120 return L->data[L->front]; 121 } 122 int getLength(SequenceQueue *L){ 123 return (L->rear-L->front+MAXSIZE)%MAXSIZE; 124 } 125 status push(SequenceQueue *L, datatype node_to_push){ 126 //node_to_insert表示想要入隊的元素 127 if ((L->rear+1)%MAXSIZE == L->front) return ERR; //隊列已滿 128 L->data[L->rear]=node_to_push; //將新元素入隊 129 L->rear=(L->rear+1)%MAXSIZE; //指針rear后移 130 return OK; 131 } 132 datatype pop(SequenceQueue *L){ 133 datatype q; 134 if (isEmpty(L)) return ERR; //隊列是空 135 q=L->data[L->front]; //將要出隊的元素先賦值給臨時變量s 136 L->front=(L->front+1)%MAXSIZE; //指針front后移 137 return q; //返回出隊的元素的值 138 } 139 void showQueue(SequenceQueue *L){ 140 int i; 141 int total=getLength(L); 142 for (i=0; i<total; i++){ 143 printf("%c\t",L->data[ (L->front+i)%MAXSIZE ]); 144 } 145 } 146 /* 147 隊列的定義:只允許在一端進行插入,另一端進行刪除的線性表,也是一種操作受限的線性表 148 一般,把允許插入的一端叫做隊尾,允許刪除的一端叫做隊頭 149 不含任何元素的隊列就是空隊 150 所以,隊列又稱先進先出(First in First out)的線性表 151 */ 152 /* 環境: Code::Blocks with GCC 5.1 */
運行截圖: