棧和隊列的定義和特點
1、棧
-
棧和隊列是限定插入和刪除只能在表的“端點”進行的線性表
-
表尾稱為棧頂(top),表底稱為棧底(bottom)
-
不含有元素的空表稱為空棧
-
與線性表不同,棧插入的只能插入在最后的位置,刪除也只能刪除最后的位置(后進先出)
-
一般用於解決下列的問題
- 數制轉換
- 表達式求值
- 括號匹配檢驗
- 八皇后問題
- 行編輯程序
- 函數調用
- 迷宮求解
- 遞歸調用的實現等
-
abc三個元素按照abc的順序入棧,得到的出棧順序只可能是cba, abc, acb, bac, bca幾種,注意這里不限制出棧的時機,不一定要等到所有的元素全部入棧才能出站,只要入棧的順序保持abc即可。
-
棧與線性表的區別
一般線性表 | 棧 | |
---|---|---|
邏輯結構 | 一對一 | 一對一 |
存儲結構 | 順序表、鏈表 | 順序表、鏈棧 |
運算規則 | 隨機存取 | 后進先出(LIFO) |
2、隊列
- 隊列是一種先進先出的線性表
- 插入的時候只能插入在隊尾(rear),刪除的時候只能在隊頭(front)刪除(先進先出)
- 一般用於解決類似下列的問題
- 脫機打印輸出
- 多用戶系統中,用於用戶排成隊,分時地循環使用CPU和主存
- 按用戶的優先級排成多個隊,每個優先級一個隊列
- 實時控制系統中,信號按接收的先后順序依次處理
- 網絡電文傳輸,按到達時間先后順序依次進行等
- 隊列的特點
隊列 | |
---|---|
邏輯結構 | 一對一 |
存儲方式 | 順序隊、鏈隊 |
運算規則 | 先進先出 |
實現方式 | 入隊和出隊操作,具體實現依順序隊或鏈隊的不同而不同 |
棧與隊列的應用案例
- 1.進制的轉換,如將十進制轉換為八進制數
- 將十進制數除以8,然后將余數分別入棧,最后在出棧,出棧的順序就是最終轉換為8進制的結果
- 2.括號匹配的檢驗,括號在實際使用的時候一定是成對出現的,現在可以使用過堆棧來檢測使用的括號是否是成對出現的
- 將左括號分別入棧,當檢測到一個有括號的時候,就將棧頂的元素與之對應 出棧一個,如果存在不匹配的情況,那么入棧的和出棧之間就不能配對,這樣就起到了檢測括號是否成對的效果。
- 3.表達式求值,實際應用中,加減乘除與括號優先級不同,需要按照優先級進行計算,此時可以使用棧來解決優先級問題
- 使用兩個棧,一個存放運算符,另外一個存放運算數
- 當掃描的是運算數的時候直接壓入棧
- 當時運算符的時候
- 若這個運算符比運算符(OPTR)棧頂優先級高,則入棧繼續向后處理
- 若這個運算符比OPTR棧頂運算符優先級低,則從OPTR棧中彈出兩個運算數,從棧OPTR中彈出棧頂運算符進行運算,並將運算結果壓入存放運算數(OPND)的棧
- 繼續處理當前字符,直到遇到結束符為止
- 實際上還能將標准的運算式轉換為中綴表達式,僅需要使用一個棧即可完成運算
- 舞伴問題
- 問題:假設在舞會上,男士和女士各自排成一隊,舞會開始時,依次從男隊和女隊的隊頭各出一人配成舞伴,如果兩隊初始人數不相同,則較長的那一隊中未配對者等待下一輪舞曲。
- 思路:首先構造兩個隊列,依次將隊頭元素出隊配成舞伴,某隊為空,則另外一對等待着則是下一舞曲第一個可獲得舞伴的人
棧的表示和操作的實現
- 棧的抽象數據類型定義(后面將會在順序和鏈式結構中實現)
// 1、初始化堆棧
InitStack(&s)
// 2、銷毀棧操作
DestroyStack(&S)
// 3、判定S是否為空棧
StackEmply(S)
// 4、求棧的長度
StackLength(S)
// 5、取棧頂元素
GetTop(S, &e)
// 6、棧置空操作
ClearStack(&S)
// 7、入棧操作
Push(&S, e)
// 出棧操作
Pop(&S, &e)
- 1、棧在順序表中的實現(順序棧)
利用一組地址連續的存儲單元依次存放自棧底到棧頂的數據元素,棧底一般在低地址端- 附設top指針,指示棧底元素在順序棧中的位置
- 另設base指針,指示棧底元素在順序棧中的位置
//順序棧的存儲結構
#define MAXSIZE 100
typedef struct
{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
- base 為棧底指針,初始化完成后,棧底指針base始終指向棧底的位置,若base的值為NULL,則表明棧結構不存在。top為棧頂指針,其初值指向棧底。每當插入新的棧頂元素時,指針top增1; 刪除棧頂元素時,指針top減l。因此,棧空時,top和base的值相等,都指向棧底;棧非空時,top始終指向棧頂元素的上一個位置。
- stacksize指示棧可以使用的最大容量
- 兩個指針相減,相當於獲得兩個元素之間差幾個元素,也就是兩個元素之間的元素個數。
//順序棧的初始化
Status InitStack(SqStack &S)
{
S.base = new SElemType[MAXSIZE]; //c++中,使用該方法刪除的時候需要使用delete S;
if(!S.base)
exit(OVERFLOW); //存儲空間分配失敗
S.top = S.base;
S.stacksize = MAXSIZE;
return OK;
}
//判斷棧是否為空
Status StackEmpty(SqStack S)
{
if(S.top == S.base)
return TRUE;
else
return FALSE;
}
//求順序棧的長度
int StackLength(SqStack S)
{
return S.top-S.base; //直接使用棧頂減去棧底就是棧的長度
}
//清空順序棧,棧仍然保留,但是里面的元素為空,不用將所有的數據刪除,
//直接將指針指向棧底即可,新數據入棧時自然會將就數據覆蓋掉
Status ClearStack(SqStack &S)
{
if(S.base)
S.top = S.base;
return OK;
}
//銷毀一個棧,與清空不一樣,空間全部需要釋放了
Status DestroyStack(SqStack &S)
{
if(S.base)
{
delete S.base; //釋放空間
S.stacksize = 0;
S.base = S.top = NULL;
}
return OK;
}
//順序棧的入棧操作,在棧頂插入元素e
Status Push(SqStack &S, SElemType e)
{
if(S.top-S.base == S.stacksize) //判斷棧是否滿了
return ERROR; //棧已滿,上溢
*S.top++ = e; //兩步合成一步 *S.top = e; S.top++;存完后指針后一位
return OK;
}
//出棧操作
Status Pop(SqStack &S,SElemType &e)
{
//刪除s的棧頂元素,用e返回刪除的值
if(S.top == S.base)
return ERROR;
e = *--S.top; //棧頂指針是指向最上面元素在往后一個位置的,所以要先--才能訪問到棧頂的值
return OK;
}
//取出順序棧的棧頂元素
SElemType GetTop(SqStack S)
{
//返回棧頂元素,不修改棧頂指針
if(S.top != S.base) //棧非空
return *(S.top - 1); //返回棧頂元素的值,棧頂指針不變
}