隊列的順序存儲結構(循環隊列)(C語言實現)


  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 */

 

運行截圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM