資料結構與演算法分析 - 2 - 棧ADT
1.描述:實質是一種受到限制的表,即插入刪除只能在表的末端,能夠實現LIFO(後進先出)
2.棧的實現
連結串列實現(鏈棧)
陣列實現(順序棧)
3.鏈棧
建立一個空棧
1 struct Node 2 { 3 int value; 4 Node *next; 5 }; 6 7 //建立了一個頭節點為S的鏈棧,S將一直表示棧頂元素 8 Node *CreatStack() 9 { 10 Node *S = new Node; 11 S->next = NULL; 12 return S;13 }
測試棧是否為空
1 int IsEmpty(Node *S) 2 { 3 return S->next == NULL; 4 }
從棧頂彈出元素
1 void Pop(Node *S) 2 { 3 Node *FirstCell; 4 if (IsEmpty(S)) //空棧,將返回異常 5 cout << "Empty Stack" << endl; 6 else 7 { 8 FirstCell = S->next; //取出棧頂9 S->next = S->next->next; //使S指向新的棧頂 10 delete FirstCell; //刪除原棧頂,完成出棧 11 } 12 }
返回棧頂元素(不彈出,不改變棧頂指標),遇到空棧將返回異常
1 int Top(Node *S) 2 { 3 if (!IsEmpty(S)) 4 return S->next->value;
5 }
清空棧(同時銷燬實體記憶體)
1 void MakeEmpty(Node *S)2 { 3 while (!IsEmpty(S)) 4 Pop(S); 5 }
壓棧
1 void Push(int x, Node *S) 2 { 3 Node *TempCell = new Node; 4 if (TempCell == NULL) 5 cout << "Out of space" << endl; 6 else 7 { 8 TempCell->value = x; //把x賦值給value 9 TempCell->next = S->next; //將新節點設為棧頂 10 S->next = TempCell; //使S指向新的棧頂 11 } 12 }
遍歷棧
1 void Print(Node *S) 2 { 3 Node *p = S->next; //將p初始化為棧頂 4 while (p != NULL) 5 { 6 cout << p->value << endl; 7 p = p->next; 8 } 9 }
注意,記憶體分配操作的時間開銷較大(C的malloc和free,C++的new和delete),可用通過建立第二個棧來避免,第一個棧中的元素彈出時將不會被刪除,而是暫時存放在第二個棧中,然後當第一個棧需要新的元素時,將首先在第二個棧中查詢,由於在鏈棧中,所有的操作均花費常數時間,所以檢索第二個棧的開銷相對於記憶體分配是值得的。
宣告一個Stack連結串列
1 struct Stack 2 { 3 Node *top; //top指向棧頂 4 Node *bottom; //bottom指向棧底 5 };
這樣在任意Node處都可以通過p->top或者p->bottom的方式獲得當前棧的棧頂和棧底
判斷棧是否為空也可以這樣進行
1 int IsEmpty(Node *p) 2 { 3 if (p->top == p->bottom) //棧頂和棧底相同 4 return 1; 5 else 6 return 0; 7 }
4.順序棧
用陣列實現的順序棧避免了使用指標,順序棧使用一個一維陣列來存放棧的元素
每一個棧有一個TopOfStack,對於空棧來說TopOfStack = -1
壓棧時,將TopOfStack加1,Stack[TopOfStack] = x;出棧時,減1
1 typedef struct StackRecord 2 { 3 int capacity; //棧大小 4 int TopOfStack; //棧頂 5 int *Array; //陣列地址 6 }Stack;
對空棧的pop和滿棧的push都將引起程式崩潰
建立棧,陣列實現
建立空棧
1 void MakeEmpty(Stack *S) 2 { 3 S->TopOfStack = -1; 4 }
建立棧(不考慮記憶體申請失敗)
1 Stack* CreatStack(int MaxSize) 2 { 3 StackRecord *S = new StackRecord; 4 S->Array = new int[MaxSize]; 5 S->capacity = MaxSize; 6 MakeEmpty(S); 7 return S; 8 }
檢測是否為空棧
1 int IsEmpty(Stack *S) 2 { 3 return S->TopOfStack == -1; 4 }
檢測是否為滿棧
1 int IsFull(Stack *S) 2 { 3 return S->TopOfStack == MaxSize - 1; 4 }
進棧
1 void Push(int x, Stack *S) 2 { 3 if (IsFull(S)) //棧滿將丟擲異常 4 ; 5 else 6 S->Array[++S->TopOfStack] = x;
7 //S->TopOfStack++;
8 //S->Array[TopOfStack] = x;
9 }
返回棧頂元素
1 int Top(Stack *S) 2 { 3 if (!IsEmpty(S)) 4 return S->Array[S->TopOfStack]; 5 else 6 ; 7 }
出棧
1 void Pop(Stack *S) 2 { 3 if (IsEmpty(S)) 4 ; 5 else 6 S->TopOfStack--; 7 }
棧的遍歷
以遍歷陣列的方式遍歷即可
釋放棧
1 void DisposeStack(Stack *S) 2 { 3 if (S != NULL) 4 { 5 delete S->Array; 6 delete S; 7 } 8 }
5.鏈棧與順序棧
順序棧比鏈棧操作簡便,避免了指標,但對儲存空間利用效率低,大小固定,不如連結串列實現靈活
陣列實現時,宣告的棧太小,會造成棧溢位,太大則造成空間浪費
6.棧的應用
挖坑待填
參考資料:《資料結構與演算法分析——C語言描述》 Mark Allen Weiss