07.棧(一)棧的儲存結構及操作,07儲存結構操作
一、棧1.棧(stack):是限定僅在表尾進行插入和刪除操作的線性表。其中,允許插入和刪除的一端被稱為棧頂(top),另一端被稱為棧底(bottom),不含任何資料元素的棧被稱為空白棧。棧又被稱為後進先出(Last In First Out)的線性表,簡稱LIFO結構。 棧的插入操作為進棧,棧的刪除操作為出棧。2.棧的抽象資料類型ADT 棧(stack)Data 同線性表。元素具有相同類型,相鄰元素具有前驅和後繼關係。Operation InitStack(*S):初始化操作,建立一個空棧S。 DestoryStack(*S):若棧存在,則銷毀它。 ClearStack(*S):將棧清空。 StackEmpty(S):若棧為空白,返回true,否則返回false。 GetTop(S,*e):若棧存在且非空,用e返回S的棧頂元素。 Push(*S,e):若棧S存在,插入新元素e到棧S中並稱為棧頂元素 Pop(*S,*e):刪除棧S中棧頂元素,並用e返回其值 StackLength(S):返回棧S的元素個數endADT
二、棧的儲存結構1.棧的順序儲存結構及實現(複雜度均為O(1))(1)棧順序結構定義#define OK 1#define ERROR 0typedef int SElemType; //SElemType類型根據實際情況而定,這裡假設為inttypedef struct{ SElemType data[MAXSIZE]; //棧儲存空間大小MAXSIZE int top; //用於棧頂指標}SqStack;(2)進棧操作(從棧頂插入一個元素)演算法思路:a.判定是否棧滿;b.棧頂加1;c.將元素插入到實現:插入元素e為新的棧頂元素
Status Push(SqStack *S,SElemType e){ if(S->top==MAXSIZE-1) //棧頂=MAXSIZE-1,即數組的最後一個儲存位置,說明棧滿 { return ERROR; } S->top++; //棧頂加1 S->data[S->top]=e; //將元素入棧 return OK;}
(3)出棧操作(從棧頂刪除一個元素)演算法思路:a.判定棧是否為空白;b.將棧頂元素儲存到指標變數e所指向的儲存位置中;c.棧頂減1.實現:若棧不為空白,則刪除S的棧頂元素,用e返回其值,並返回OK;否則返回ERROR
Status Pop(SqStack *S,SElemType *e){ if(S->top==-1) //空棧 { return ERROR; } *e=S->data[S->top]; S->top--; return OK;}
(4)兩棧共用空間 棧的順序儲存只准棧頂進出元素,所以不存線上性表插入和刪除時需要移動大量的元素,但是有個缺陷是必須事先確定數組儲存空間大小。當兩個棧資料類型一樣是,我們可以將兩個棧所開闢的空間共用使用。
A.思想:將棧1的棧底,即為共用空間的棧底(下標為0);另一個棧的棧底為共用棧的末端(下標為數組長度n-1處),其中top1和top2是棧1和棧2的棧頂指標。對於共用棧,若棧2是空棧(top2=n),棧1的top1=n-1,說明棧1滿了;若棧1是空棧(top=-1),棧2的top2=0,說明棧2滿了。即共用棧滿條件:top1+1==top2。B.共用棧空間結構typedef struct{ SElemType data[MAXSIZE]; //定義一個數組儲存空間,大小為MAXSIZE即為棧大小 int top1; //棧1棧頂指標 int top2; //棧2棧頂指標}SqDoubleStack;C.共用棧元素入棧操作演算法思路:a.首先判斷共用棧是否棧滿;b.通過stackNumber參數判斷插入哪個棧 若為棧1,則棧頂top1增1,再將元素e入棧;若為棧2,則棧頂top2減1,在將元素e入棧。實現:插入元素e為新的棧頂元素
Status Push(SqDoubleStack *S,SElemType e,int stackNumber){ if(S->top1+1==S->top2) //判定共用棧是否棧滿(top1+1=top2) { return ERROR; } if(stackNumber==1) //棧1有元素進棧,棧頂top1加1 S->data[++S->top1]=e; else if(stackNumber==2) S->data[--S->top2]=e; //棧2有元素進棧,棧頂top2減1 return OK;}
D.共用棧元素出棧操作演算法思想:a.判斷哪一個棧;b.若為棧1,則先將元素賦值給指標變數e指向的空間位置,再棧頂top1減1; 若為棧2,則先將元素賦值給指標變數e指向的空間位置,再棧頂top2增1;實現:若棧不空,則刪除S的棧頂元素,用e返回其值,並返回OK;否則返回ERROR
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber){ if(stackNumber==1) //若刪除元素屬於棧1 { if(S->top1==-1) //空棧 return ERROR; *e=S->data[S->top1--]; //將元素賦值給e,棧頂再減1 } else if(stackNumber==2)//若刪除元素屬於棧1 { if(S->top2==MAXSIZE) //空棧 return ERROR; *e=S->data[S->top2++]; //將元素賦值給e,棧頂再減1 } return OK;}
升華筆記1:1.棧的順序儲存結構是通過數組來實現的,下標為0為棧底,因為首元素都哦存在棧底變化最小。當棧存在一個元素時,棧頂top為0,如果為空白棧則top=-1,這也是判斷棧是否為空白棧的條件。另外,要區別儲存棧的長度StackSize(MAXSIZE)和儲存位置。2.兩棧共用空間只適用於元素資料類型相同的兩個棧,且入棧是先修改棧頂再元素入棧;出棧是先元素出棧再修改棧頂。
2.棧的鏈式儲存結構及實現
(1)鏈棧的結構//鏈結點:包含資料域和指標域typedef struct StackNode{ SElemType data; //資料域 struct StackNode *next; //指標域}StackNode,*LinkStackPtr;//鏈棧結構typedef struct LinkStack{ LinkStackPtr top; //鏈棧頭結點 int count; //結點個數}LinkStack;
(2)鏈棧的進棧操作演算法思想:a.先為新結點s開闢一段空間,空間的大小為結點結構大小;b.將要插入的元素值e賦值給新結點s的資料域;c.將原來的棧頂元素賦值給新結點s的後繼d.使棧頂指標指向新結點ee.鏈棧元素增1實現:插入元素e為新的棧頂元素
Status Push(LinkStack *S,SElemType e){ LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode)); //開闢一個新的結點空間,大小為結構體StackNode大小 s->data=e; //將元素e賦值給新結點s的資料域 s->next=S->top;//把當前的棧頂元素賦值給新結點的直接後繼 S->top=s; //將新的結點s賦值給棧頂指標 S->count++;//鏈棧元素數目加1 return OK;}
(3)鏈棧的出棧操作演算法思想:a.首先判定鏈棧是否為空白棧b.將棧頂指標指向元素資料域資料賦值給ec.將棧頂指標原先指向的元素賦值給pd.再將棧頂指標指向後一個結點,釋放pe.鏈棧元素數目減1實現:若棧不空,則刪除S的棧頂元素,用e返回其值,並返回OK
Status Pop(LinkStack *S,SElemType *e){ LinkStackPtr p; if(StackEmty(*S)) //若空棧 return ERROR; *e=S->top->data; //將棧頂元素資料賦值給指標變數e指向空間 p=S->top; //將棧頂指標指向的結點(棧頂元素)賦值給p S->top=S->top->next; //使得棧頂指標下移一位,即此時棧頂指標指向後一個結點 free(p); //釋放結點p S->count; //鏈棧元素數目減1 return OK;}
3.效能分析(1)時間複雜度:棧的順序結構和鏈式結構均為O(1);(2)空間複雜度 棧的順序結構只有資料域,空間複雜度小,但事先需要確定一個固定的長度,可能會存在記憶體空間浪費問題; 棧的鏈式儲存結構每個元素具有資料域和指標域,空間複雜度稍複雜,同時也增加了一些記憶體開銷,但對於棧的長度無限制。(3)如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那麼最好是用鏈棧;反之,如果它的變化在可控範圍內,建議使用順序棧效能會更高些。