一、棧的定義
棧是只能在一端進行插入和刪除操作的線性表。表中允許進行插入、刪除操作的一端稱爲棧頂。棧頂的位置是動態的,由一個稱爲棧頂指針的位置指示器指示。表的另一端稱爲棧底。
當棧中沒有元素時,稱爲空棧。棧的插入操作通常稱爲入棧或者進棧,棧的刪除操作一般稱作出棧或者退棧。
棧的主要特點是 ”後進先出“ ,即後進棧的元素先出棧。每次進棧的元素都放在原來棧頂元素之前,成爲新的棧頂元素,每次出棧的元素都是當前棧頂元素。所以棧也被稱爲後進先出表。
棧的抽象數據類型定義如下:
ADT List { 數據對象: D={a(i)|1=<i<=n, a(i)是 ElemType類型} 數據關係: R={<a(i), a(i+1)>|a(i),a(i+1)屬於 D, i=1,2,3,···,n-1} 基本運算: InitStack(&s): 初始化棧,構建一個空棧 s ClearStack(&s): 銷燬棧:釋放棧 s所佔的內存空間 StackLength(s): 求棧的長度: 返回棧 s中的元素個數 StackEmpty(s): 判斷棧是否爲空:若棧 s爲空則返回真,否則返回假 Push(&s, e): 進棧:將元素 e添加到棧頂 Pop(&s, &e): 出棧:將棧頂元素賦值給 e並從棧頂刪除 GetTop(s, &e): 取棧頂元素:將當前棧頂元素的值賦值給 e DispStack(s):顯示棧中所有元素的值:按照從棧頂到棧底的順序顯示棧中所有元素的值 }
二、棧的順序存儲結構與基本運算
使用順序存儲結構存儲棧時,我們會分配一塊連續的內存空間來存放棧中的元素,並用一個變量指向當前的棧頂。採用順序存儲結構的棧稱爲順序棧。
定義順序棧的結合體時,我們假設棧能夠存儲的最大元素數量爲 MaxSize,指向棧頂的變量爲 STop(在順序存儲結構中 STop是一個整數,表示當前棧頂元素的下標),存放棧中元素的數組 data。
所以我們可以定義棧的結構體爲:
#define MaxSize 100 typedef char ElemType; typedef struct Stack{ int STop; ElemType data[MaxSize]; }Stack;
這裏棧的最大大小我們通過宏來定義,在實現時也可以使用其他的方法定義,比如在棧中再加一個 MaxSize,data改爲一個 ElemType[] 類型的指針,在初始化棧時,通過 malloc函數讀取 MaxSize來爲 data分配內存。
1、棧的初始化
void InitStack(Stack* &s) { s = (Stack*)malloc(sizeof(Stack)); s->STop = -1; }//InitStack
在初始化函數裏我我們把 s->STop賦值爲 -1,以此來標記棧 s是空棧。
也可以把 STop賦值爲 0,這與 -1區別不大,只是當初始化爲 -1時 STop總是指向棧頂元素,而初始化爲 0時 STop總是指向棧頂元素的前一位。
2、銷燬棧
void ClearStack(Stack* &s) { free(s); s = NULL; }//ClearStack
3、求棧的長度
int StackLength(Stack* s) { return s->STop + 1; }//StackLengt
這裏我們返回 STop+1是因爲 STop總是指向棧頂元素,而棧頂元素的下標就是數組元素個數減一。
當 STop初始化爲 0時我們就可以直接返回 STop。
4、判斷棧是否爲空
bool StackEmpty(Stack* s) { if (s->STop < 0) { return true; } else{ return false; } }//StackEmpty
5、進棧
void Push(Stack* &s, ElemType e) { s->STop++; if (s->STop > MaxSize || s->STop < 0) { throw; } else{ s->data[s->STop] = e; } }//Push
6、出棧
void Pop(Stack* &s, ElemType &e) { if (!StackEmpty(s)) { e = s->data[s->STop]; s->STop--; } else { throw; } }//Pop
7、取棧頂元素
void GetTop(Stack* s, ElemType &e) { if (!StackEmpty(s)) { e = s->data[s->STop]; } else { throw; } }//GetTop
8、打印棧
void DispStack(Stack* s) { if (StackEmpty(s)) { printf("stack is empty.\n"); } else { int top = s->STop; while (top >= 0) { printf("index: %d, value: %c\n", top, s->data[top]); top--; } } }//DispStack
三、棧的鏈式存儲結構與基本運算
棧的鏈式存儲結構叫做鏈棧,通常用單鏈表實現。鏈棧的有點是不用考慮滿棧的情況,操作也很方便,我們只要在頭節點處操作就行。最靠近頭節點的節點是棧頂節點,而鏈表中與頭節點相對的一端則是棧底。
鏈棧中節點的結構定義如下:
typedef char ElemType; typedef struct LinkStack { ElemType data; struct LinkStack* next; }LinkStack;
1、鏈棧的初始化
void InitStack(LinkStack* &s) { s = (LinkStack*)malloc(sizeof(LinkStack)); s->next = NULL; }
2、銷燬鏈棧
void ClearStack(LinkStack* &s) { while(s->next != NULL) { LinkStack* temp = s->next->next; free(s->next); s->next = temp; } free(s); s = NULL; }
3、求鏈棧的長度
int StackLength(LinkStack* s) { int i = 0; while (s->next != NULL) { i++; s->next = s->next->next; } return i; }
4、判斷鏈棧是否爲空
bool StackEmpty(LinkStack* s) { return s->next == NULL; }
5、進棧
void Push(LinkStack* &s, ElemType e) { LinkStack* new_node = (LinkStack*)malloc(sizeof(LinkStack)); if (new_node == NULL) { throw; } new_node->data = e; new_node->next = s->next; s->next = new_node; }
鏈棧進棧與單鏈表添加節點很相似,只是鏈棧進棧時我們只在頭節點後面添加節點,而單鏈表則可以在鏈表的任意位置添加節點。
6、出棧
void Pop(LinkStack* &s, ElemType &e) { if (StackEmpty(s)) { throw; } LinkStack* temp = s->next; e = temp->data; s->next = temp->next; free(temp); }
7、取棧頂元素
void GetTop(LinkStack* s, ElemType &e) { if (StackEmpty(s)) { throw; } e = s->next->data; }
8、打印棧
void DispLStack(LinkStack* s) { LinkStack* temp = (LinkStack*)malloc(sizeof(LinkStack)); *temp = *s; int i = 0; while (temp->next != NULL) { i++; printf("index: %d, value: %c\n", i, temp->next->data); temp->next = temp->next->next; } }
這裏使用一個 temp作爲中間變量是爲了避免直接修改 s所指向的內存。