終於有可以有時間寫點數據結構的學習總結了,前段時間一直在緊張的忙一些項目,都沒有空出時間來學習數據結構,現在終於可以稍微喘口氣了,還是數據結構有意思,這兩天看了點棧的東西,寫下來總結一下,有錯誤的地方希望看到的朋友指出來,感激不盡。
根據學習,棧就是一種線性數據結構,棧的運算只能在表的一段進行,所以這種數據結構具有“后進先出”的特點。
接下來是棧的c語言實現。其中棧由一個top節點和bottom節點組成,這兩個節點分別指向棧的頂部和底部。其中棧的組成結點是由結構體實現,結構體由數據庫和指向下一個結點的指針域構成,下面是結點的c語言實現:
1 /* 2 定義實現棧的結點 3 */ 4 typedef struct Node{ 5 int data; 6 struct Node * pNext; 7 }NODE,* PNODE;
有了實現棧的結點,那么接下來就是該如何實現棧(具體的解釋看代碼):
1 /* 2 定義棧的結構,此處采用鏈表實現靜態棧,所以僅僅需要給出棧的頭部指針和底部指針即可 3 */ 4 typedef struct Strack{ 5 PNODE pTop; 6 PNODE pBottom; 7 }STRACK,* PSTRACK;
棧的基本操作:棧的初始化,壓棧、棧的遍歷、出棧等操作
1 /* 2 對棧初始化的函數 3 對棧初始化僅僅是令棧中的top和bottom指針指向一個確定的地址,並且此地址指向的是棧的頭結點(頭結點的引入可以 4 大大方便對棧的操作)。 5 */ 6 void init_strack(PSTRACK pS){ 7 pS->pTop=(PNODE)malloc(sizeof(NODE)); //定義一個新結點,這個結點就是棧的頭結點,並且讓pTop指向這個結點 8 if(pS->pTop==NULL){ 9 printf("內存分配失敗"); 10 exit(-1); 11 } 12 else{ 13 pS->pBottom=pS->pTop; //令bottom和top都指向頭結點,則初始化棧完成,棧中沒有任何有效結點 14 pS->pTop->pNext=NULL; 15 } 16 17 } 18 19 /* 20 壓棧函數 21 因為棧具有“先進后出、后進先出”的特性,所以,在將元素壓入棧時,也只能將棧壓入棧的頂部 22 由此,壓棧函數就是令棧的top指針指向新結點,新結點的指針域指向未壓入棧時的最頂部的元素。 23 */ 24 int push(PSTRACK pS,int val){ 25 PNODE pNew=(PNODE)malloc(sizeof(NODE)); //定義一個新結點 26 if(pNew->pNext==NULL){ 27 printf("內存分配失敗"); 28 exit(-1); 29 } 30 pNew->data= val; 31 pNew->pNext=pS->pTop; //指針賦值的順序不可以亂,為保證top所指向的地址不丟失,所以首先讓新結點的指針域指向pS->pTop所指向的結點 32 pS->pTop=pNew; //令top指針指向新的結點 33 return 0; //0表示當前push成功 34 } 35 36 /* 37 棧的遍歷函數 38 因為只是遍歷輸出,所以不能破壞棧原有的結構,所以我們只有定義一個新的結點p,讓其指向棧頂元素, 39 然后遍歷下移一個輸出其指向的元素的值 ,循環條件就是p=ps->pBottom 40 */ 41 void traverse(PSTRACK ps){ 42 PNODE p=ps->pTop; //讓新定義的結構體指針指向棧頂元素 43 while(p!=ps->pBottom){ 44 printf("%d ", p->data); 45 p=p->pNext; //讓當前指針指向當前節點的下一個結點 46 } 47 printf("\n"); 48 return; 49 } 50 51 52 /* 53 判斷當前棧是否為空的函數 54 若為空,返回true 55 否則,返回false 56 */ 57 bool isEmpty(PSTRACK ps){ 58 if(ps->pTop==ps->pBottom){ 59 return true; 60 } 61 else{ 62 return false; 63 } 64 } 65 66 /* 67 彈棧函數:彈棧簡單的說就是將第一個數值彈出,然后將ps->pTop指向原第一個結點的下一個結點(即彈棧后的第一個結點), 68 然后在將被彈出的元素在內存中釋放掉。 69 */ 70 bool pop(PSTRACK ps,int *pVal){ 71 if(isEmpty(ps)){ //判斷當前棧是否為空,若為空,則返回false 72 return false; 73 } 74 else{ 75 PNODE p=ps->pTop; //定義一個結點,這個結點在每次彈棧時都是指向棧頂,這樣可以保證不會造成被彈出元素的丟失 76 ps->pTop=p->pNext; //讓top結點指向棧頂元素 77 *pVal=p->data; 78 free(p); //將被彈出元素釋放 79 p=NULL; 80 return true; 81 } 82 }
上述就實現了對棧的基本操作,當然具體的應用還要看具體的算法要求。
上述是在大學時學習的,下面的是工作兩年后再次學習時的理解,可能會有不同的見解,都寫出來吧。
1、棧的定義:一種可以實現“先進后出”的數據存儲結構,數據的插入和刪除只能在數據的一端進行。
內存分為靜態內存和動態內存。
靜態內存在棧中分配;動態內存在堆中分配。
棧:由操作系統自動分配釋放,存放函數的參數值,
局部變量的值等。
堆:一般由程序猿分配釋放,若程序猿不釋放,程序結束時由OS回收(類似於c語言中的malloc函數分配的空間)。
2、棧的分類:
(1)靜態棧:以數組作為數據的存儲。
(2)動態棧:以鏈表作為數據的存儲方式。
3、棧的相關操作(該處采用鏈表的動態棧):
一點說明:
因為用鏈表實現棧,其實其本質就是一個鏈表,只不過對該鏈表的插入(push)和刪除(pop)操作都在該鏈表的一端進行(該段被稱為棧頂,且人為限制就只在該棧頂進行操作),所以該鏈表就會具有了“先進后出”的特性,則被稱為棧結構。
所以,嚴格來說,棧僅僅需要一個棧頂,該棧頂指向該鏈表的被稱為棧頂端的節點(所以,該棧頂也是一個該Node類型的指針)。
(1)棧中每個節點的結構:


(2)棧的結構:
因為通過棧頂可以找到該棧的所有元素,所以,該棧對應的鏈表應該是一個自上而下指向的鏈表。


該棧僅需要一個stop指針,指向棧頂元素。



(3)棧的初始化操作:


(4)棧的push操作:


(5)棧的pop操作:


(6)棧是否為空及棧的遍歷操作:


其實在棧中可以增加一個PNODE類型的指針sbuttom,讓該指針用於指向棧低節點,在判斷非空時,只要判斷sc->stop == sc->sbuttom時,就表示top和buttom重合了,指向了最底部的節點。其實,該buttom節點可以通過最后一個節點的next指針域是否為NULL來判斷(next指針域為NULL時,就表示該節點沒有下一個節點了,是棧低節點)。
代碼:
1 #include <stdio.h> 2 #include <sys/malloc.h> 3 #include <stdlib.h> 4 5 /* 6 棧結構及其操作: 7 當前采用鏈表進行棧中數據的存儲。 8 */ 9 10 11 /* 12 定義棧中每一個節點的結構 13 */ 14 typedef struct Node{ 15 int data; //數據域 16 struct Node * pnext; //指針域,指向棧中下一個節點 17 }NODE, * PNODE; //定義兩個別名,方便操作 18 19 /* 20 定義棧的結構: 21 因為用鏈表實現棧,其實其本質也是一個鏈表,只不過該鏈表的插入(push)和刪除(pop)操作只能在鏈表的一端(該端被稱作棧頂)進行操作,所以該鏈表具有“先進后出”的特性,被稱作棧(個人理解)。 22 所以,嚴格來說,棧僅僅需要一個指向棧頂(鏈表的一端)struct Node *類型的指針就可以知道該棧中的所有數據。 23 */ 24 typedef struct Stack{ 25 struct Node * stop; //棧的棧頂(指向棧中最頂部的那個元素) 26 } STACK; 27 28 //棧的初始化操作 29 struct Stack init_stack(); 30 31 //push操作 32 void stack_push(STACK *sc, int data); 33 34 //pop操作 35 int pop(STACK *sc); 36 37 //遍歷操作 38 void trvel_stack(STACK *sc); 39 40 //判斷是否為空操作 41 int is_empty(STACK *sc); 42 43 int main(int argc, char const *argv[]) 44 { 45 STACK sc; 46 sc = init_stack(); 47 48 //push幾個數據 49 stack_push(&sc, 1); 50 stack_push(&sc, 3); 51 stack_push(&sc, 7); 52 stack_push(&sc, 2); 53 stack_push(&sc, 10); 54 55 //遍歷 56 trvel_stack(&sc); 57 58 //pop一個數據出來 59 pop(&sc); 60 61 trvel_stack(&sc); 62 63 return 0; 64 } 65 66 /* 67 棧的初始化操作 68 */ 69 struct Stack init_stack(){ 70 //先定義一個棧 71 STACK sc; 72 73 //初始化棧中的第一個節點(棧低節點),該節點並沒有什么實際意義,不存放數據 74 PNODE pbottom = (PNODE)malloc(sizeof(NODE)); 75 if (pbottom == NULL) { 76 printf("內存分配失敗\n"); 77 exit(1); 78 } 79 pbottom->pnext = NULL; 80 81 //將棧頂指向該元素 82 sc.stop = pbottom; 83 84 return sc; 85 } 86 87 /* 88 棧的push操作 89 */ 90 void stack_push(STACK *sc, int data){ 91 printf("調用了!\n"); 92 93 //新創建一個節點 94 PNODE pnew = (PNODE)malloc(sizeof(NODE)); 95 printf("%p\n", pnew); 96 if (pnew == NULL) { 97 printf("內存分配失敗\n"); 98 exit(1); 99 } 100 pnew->data = data; 101 102 //將新節點指向原top節點,將棧的top指向該新節點 103 pnew->pnext = sc->stop; 104 sc->stop = pnew; 105 } 106 107 /* 108 棧的pop操作 109 */ 110 int pop(STACK *sc){ 111 int res; 112 //判斷棧是否為空 113 if (is_empty(sc) == 1){ 114 printf("棧為空,不可pop\n"); 115 exit(1); 116 } else { 117 PNODE ptmp = sc->stop; //先將需要被pop出的節點存儲起來 118 sc->stop = sc->stop->pnext; //將棧的top指向下一個節點 119 res = ptmp->data; 120 free(ptmp); //將地址釋放 121 return res; 122 } 123 } 124 125 /* 126 判斷棧是否為空的操作 127 */ 128 int is_empty(STACK *sc){ 129 PNODE p = sc->stop; 130 if (p == NULL || p->pnext == NULL) { 131 return 1; //true表示為空 132 } else { 133 return 0; //false表示非空 134 } 135 } 136 137 /* 138 遍歷棧 139 */ 140 void trvel_stack(STACK *sc){ 141 if (is_empty(sc) == 1) { 142 printf("棧為空喲,無法遍歷呢~\n"); 143 exit(1); 144 } else { 145 printf("遍歷調用了!\n"); 146 PNODE ptmp = sc->stop; 147 while (ptmp->pnext != NULL) { //直到最后一個為NULL的節點,該節點就是最后一個節點 148 printf("%d\n", ptmp->data); 149 ptmp = ptmp->pnext; 150 } 151 } 152 153 }