資料結構程式碼實現之佇列的連結串列實現(C/C++)
上班閒著無聊,一直想著要開始寫部落格,但又不知道寫什麼。最近又回顧了下資料結構的知識,那就從資料結構開始吧。
前言
關於C語言結構體的知識以及佇列的特性請讀者自行了解,此處不做過多解釋,嘻嘻。
同時此篇文章僅僅是關於佇列的連結串列實現。
第一步:結構體編寫
我們首先分析一下佇列的特徵:先進先出,隊尾插入,隊頭刪除,暫時想到的就這麼多。
首先,對於連結串列的節點結構體的內容,我們首先想到的是它有一個值,還有一個指向下
一個節點的指標(連結串列相關知識請讀者自行了解),那麼它的結構體可實現如下:
1 typedef struct Qnode{ 2 int data; 3 struct Qnode *next; 4 };
對映到圖形,其是這樣的結構:
接下來,要讓這種節點實現佇列的特性,我們可以再建立一個結構體,該結構體有一個指向隊頭節點的指標和一個指向隊尾節點的指標,那麼它的實現如下:
1 typedef struct LQueue{ 2 Qnode *front; 3 Qnode *rear; 4 };
其中,front指標指向隊頭,rear指標指向隊尾(注意,該指標是指向Qnode型別的指標)
對映到圖形,其是這樣的結構:
好了,結構體編寫工作到這裡就完成了,下面開始下一步工作。
第二步,佇列的方法分析及實現
一個佇列有哪些方法呢,根據前面提到的特性,首先要有插入和刪除的方法,我們可以定義插入操作為入隊(書上也是這麼說的),刪除操作為出隊,這兩個操作應該是佇列裡最基本的。接下來,初始化佇列的方法也是尤其必要的。然後,為了測試方便,還可以定義一個獲取佇列的長度,佇列是否為空,獲取隊頭元素值,獲取隊尾元素值以及列印佇列所有節點資料的方法。下面是對這些方法的實現。
初始化方法:void initQueue(LQueue *q);
方法描述:將建立的佇列結構(通過引數傳入該方法)的隊頭和隊尾指標都指向一個動態生成的Qnode節點,程式碼如下:
1 void initQueue(LQueue *q){ 2 q->front = q->rear = (Qnode *)malloc(sizeof(Qnode)); 3 q->front->next = NULL; 4 }
當建立了一個佇列變數,然後呼叫該方法時:
程式碼:
1 LQueue L; 2 initQueue(&L);
記憶體空間如圖:
判斷佇列是否為空方法:int empty(LQueue *t);
該方法很簡單,不做過多描述,程式碼如下:
int empty(LQueue *t){ return t->front->next == t->rear; }
入隊方法:void push(LQueue *t, int x);
方法描述:通過動態生成一個Qnode節點,然後將x賦值給該節點的data值,再將該節點插入到佇列中,程式碼如下:
1 void push(LQueue *t, int x){ 2 Qnode *s = (Qnode *)malloc(sizeof(Qnode)); 3 s->data = x; 4 s->next = NULL; 5 t->rear->next = s; 6 t->rear = s; 7 }
程式碼解釋:
第2行:動態生成一個Qnode節點,讓指標s指向它;
第3行:將傳入的x值賦值給生成的節點(s所指向)的data值;
第4行:將s所指節點的next指標置為空;
第5行:將佇列的隊尾指標的next指標指向s所指節點;
第6行:再將隊尾指標指向s節點,完成push操作。
不懂的讀者希望能自行畫圖幫助理解,其實圖一畫出來就一目瞭然了。
出隊方法:void pop(LQueue *t);
方法描述:使用該方法時,首先應判斷佇列是否為空,為空則退出,不進行出隊操作。如果佇列不空,則首先定義一個Qnode型別指標q,讓q指向隊頭節點的下一個節點(因為隊頭節點僅作為隊頭,不儲存值),把隊頭去掉的話,就是頭節點啦。然後讓隊頭節點的next指標指向q所指節點的下一個節點,再釋放掉q所指節點(q所指節點即為要出隊的節點),程式碼如下:
1 void pop(LQueue *t){ 2 if(empty(t)){ 3 cout << "LQueue is empty,can't pop.\n"; 4 return; 5 } 6 Qnode *q = t->front->next; 7 t->front->next = q->next; 8 free(q); 9 }
程式碼解釋:
第2-5行:判斷佇列是否為空,若為空則列印提示訊息後退出,不進行出隊操作;
第6行:定義一個指標q,使其指向隊頭節點的next指標所指向的節點;(前面已經解釋了,其實就是指向隊頭節點)
第7行:讓隊頭節點的next指標指向q的next指標所指向的節點;
第8行:釋放掉q所指的節點的記憶體,完成出隊操作;
還是那句話,畫圖!一步一步理解。
獲取隊頭節點的值方法:int getFront(LQueue *t);
該方法很簡單,不做過多描述,程式碼如下:
1 int getFront(LQueue *t){ 2 return t->front->next->data; 3 }
獲取隊尾節點的值方法:int getRear(LQueue *t);
該方法很簡單,不做過多描述,程式碼如下:
1 int getRear(LQueue *t){ 2 return t->rear->data; 3 }
獲取佇列長度的方法:int getSize(LQueue *t);
方法描述:使用一個指向頭結點的指標,不斷遍歷,每遍歷一次,計數器加1,當該指標指向空時,遍歷完成,返回該計數器,程式碼如下:
1 int getSize(LQueue *t){ 2 Qnode *q = t->front->next; 3 int k = 0; 4 while(q){ 5 k++; 6 q = q->next; 7 } 8 return k; 9 }
程式碼解釋:
第2行:定義一個指向隊頭節點的指標q;
第3行:定義一個計數器k;
第4-7行:該程式碼為,當q不指向NULL時,k+1,然後q指向下一個節點,繼續迴圈判斷。
第8行:當迴圈結束時,返回該計數器k,即為佇列的長度。
列印佇列所有值方法:void printQueue(LQueue *t);
方法描述,定義一個指向Qnode型別的指標,進行遍歷,每遍歷一個節點,列印該節點,然後繼續遍歷下一節點,程式碼如下:
1 void printQueue(LQueue *t){ 2 Qnode *q = t->front->next; 3 while(q){ 4 cout << q->data << " "; 5 q = q->next; 6 } 7 cout << "\n"; 8 }
該程式碼比較簡單,不做過多解釋。
好了,方法至此已全部完成,接下來,就可以通過main函式進行測試了。
第三步:編寫main方法測試執行
完整程式碼如下,親測可用,希望各位新入坑的朋友多多敲程式碼練習哦:
1 #include <iostream> 2 using namespace std; 3 4 typedef struct Qnode{ 5 int data; 6 struct Qnode *next; 7 }; 8 9 typedef struct LQueue{ 10 Qnode *front; 11 Qnode *rear; 12 }; 13 14 void initQueue(LQueue *q){ 15 q->front = q->rear = (Qnode *)malloc(sizeof(Qnode)); 16 q->front->next = NULL; 17 } 18 19 int empty(LQueue *t){ 20 return t->front->next == t->rear; 21 } 22 23 void push(LQueue *t, int x){ 24 Qnode *s = (Qnode *)malloc(sizeof(Qnode)); 25 s->data = x; 26 s->next = NULL; 27 t->rear->next = s; 28 t->rear = s; 29 } 30 31 void pop(LQueue *t){ 32 if(empty(t)){ 33 cout << "LQueue is empty,can't pop.\n"; 34 return; 35 } 36 Qnode *q = t->front->next; 37 t->front->next = q->next; 38 free(q); 39 if(t->rear == NULL) 40 t->rear = t->front; 41 } 42 43 int getFront(LQueue *t){ 44 return t->front->next->data; 45 } 46 47 int getRear(LQueue *t){ 48 return t->rear->data; 49 } 50 51 int getSize(LQueue *t){ 52 Qnode *q = t->front->next; 53 int k = 0; 54 while(q){ 55 k++; 56 q = q->next; 57 } 58 return k; 59 } 60 61 void printQueue(LQueue *t){ 62 Qnode *q = t->front->next; 63 while(q){ 64 cout << q->data << " "; 65 q = q->next; 66 } 67 cout << "\n"; 68 } 69 int main(){ 70 LQueue L; 71 initQueue(&L); 72 cout << "Push data to Queue...\n"; 73 push(&L,2); 74 push(&L,5); 75 push(&L,4); 76 push(&L,3); 77 push(&L,6); 78 push(&L,8); 79 push(&L,10); 80 push(&L,11); 81 cout << "Push finished.\n"; 82 cout << "You have pushed such data:"; 83 printQueue(&L); 84 cout << "Pop data out of Queue...\n"; 85 pop(&L); 86 cout << "Pop finished.\n"; 87 cout << "Now the Queue have such data:"; 88 printQueue(&L); 89 cout << "Get Queue's front data:" << getFront(&L) << endl; 90 cout << "Get Queue's rear data:" << getRear(&L) << endl; 91 cout << "Get Queue's size:" << getSize(&L) << endl; 92 pop(&L); 93 pop(&L); 94 pop(&L); 95 cout << "After poped 3 times:"; 96 printQueue(&L); 97 cout << "Judge the Queue is null or not(0 means not null,others means null):" << empty(&L) << endl; 98 pop(&L); 99 pop(&L); 100 pop(&L); 101 pop(&L); 102 cout << "After poped 4 times:"; 103 printQueue(&L); 104 cout << "Judge the Queue is null or not(0 means not null,others means null):" << empty(&L) << endl; 105 106 return 0; 107 }
人生中的第一篇部落格,寫的不好還請海涵~~祝大家生活愉快~~