以前咱們學過了普通的線性表,接下來咱們來了解一下兩種特殊的線性表——棧和隊列。編程
棧是隻容許在一端進行插入或刪除的線性表。數組
棧的順序存儲結構也叫做順序棧,對於棧頂指針top,當棧爲空棧時,top=-1;當棧爲滿棧時,top=MaxSize-1。順序棧的定義爲:數據結構
#define MaxSize 50 //定義棧中元素的最大個數 typedef struct{ Elemtype data[MaxSize]; //存放棧中元素 int top; //棧頂指針 }SqStack; //順序棧的簡寫
順序棧的入棧操做爲:函數
bool Push(SqStack &S,ElemType x){ if(S.top==MaxSize-1) return false; S.data[++S.top]=x; return true; }
順序棧的出棧操做爲:spa
bool Pop(SqStack &S,ElemType &x){ if(S.top==-1) return false; x=S.data[S.top--]; return true; }
順序棧讀取棧頂元素爲:指針
bool GetTop(SqStack S,ElemType &x){ if(S.top==-1) return false; x=S.data[S.top]; return true; }
順序棧讀取棧頂元素與出棧操做對比,注意讀取棧頂元素時棧頂指針沒有自減。code
對於一個數組,若是隻從數組頭部開始入棧,並無達到很高的空間利用率,所以咱們引入共享棧的概念。共享棧是兩個棧共用同一個數組,分別從數組頭部和數組尾部開始開始入棧。其定義爲:blog
#define MaxSize 100 //定義棧中元素的最大個數 typedef struct{ Elemtype data[MaxSize]; //存放棧中元素 int top1; //棧1頂指針 int top2; //棧2頂指針 }SqDoubleStack; //共享棧的簡寫 bool Push(SqStack &S,ElemType x,int stackNum){ if(S.top1+1==S.top2) return false; if(stackNum==1) S.data[++S.top1]=x; else if(stackNum==2) S.data[--S.top2]=x; return true; }
共享棧的滿棧條件是top1+1=top2。遞歸
同時,棧也有鏈式存儲結構,叫做鏈棧,它空棧時top=NULL,通常不會滿棧。鏈棧的定義爲:隊列
typedef struct SNode{ ElemType data; //數據域 struct SNode *next; //指針域 }SNode,*SLink; //鏈式棧的結點 typedef struct LinkStack{ SLink top; //棧頂指針 int count; //鏈式棧結點數 }LinkStack;
棧有着豐富的應用場景,比較經典的題目有括號的匹配以及後綴表達式的求值。
①括號的匹配:給你一串雜亂的大,中,小括號的序列,讓你判斷這裏的括號序列滿不知足數學計算的規律(括號套括號)。主要思路是將全部的左括號依次入棧,碰到右括號出棧匹配,如果不一樣種的括號返回false,如果同一種括號則繼續以上操做,直到空棧而且數組中的字符串遍歷完成,返回true。
②後綴表達式的計算:後綴表達式是計算機最喜歡的一種表達式方式,例如(5+10+1*13)/14這個中綴表達式,它的後綴表達式爲5 10 + 1 13 * + 14 /,其將每一步計算的運算符號置後。利用棧計算後綴表達式的主要思路是:①將數字5入棧,再將數字10入棧;②當指針碰到運算符+時,將棧中的前兩個元素出棧進行計算(後出棧的數在前位),結果入棧,如本例值15入棧;③同上一步,計算1*13得13入棧,此時棧底值15,棧頂值13;④同上一步,指針碰到運算符+,將15與13求和,得值28入棧;⑤再同上一步,計算28/14=2入棧;⑥此時字符串數組遍歷完畢,棧中的惟一值2則爲表達式的結果。
棧思想的最重要的一個應用即是遞歸,遞歸是指在一個函數、過程或數據結構中的定義中又應用了它自身的編程思想。理解遞歸的一個基礎方法即是找到遞歸式和遞歸邊界,咱們來看兩個例子:
①用遞歸求n的階乘:
int F(int n){ if(n==0) return 1; //遞歸邊界 else return n*F(n-1); //遞歸式 }
②求斐波那契數列的第n項:
int Fib(int n){ if(n==0) return 0; //遞歸邊界 else if(n==1) return 1; //遞歸邊界 else return Fib(n-1)+Fib(n-2); //遞歸式 }
遞歸式是每一次遞歸過程的計算核心,而遞歸邊界就是每一次遞歸的結束標誌。
另外一種特殊的線性表是隊列,隊列的定義是隻容許在一端進行插入,而在另外一端進行刪除的線性表。
隊列的順序存儲結構是順序隊列,其定義爲:
#define MaxSize 50 //定義隊列中元素的最大個數 typedef struct{ Elemtype data[MaxSize]; //存放隊列中元素 int front,rear; //隊頭指針和隊尾指針 }SqQueue;
但對於一個數組隊列來講,每一次入隊和出隊都會浪費一個數組位置,因而咱們引入循環隊列的概念,隊尾指針rear在指到隊尾後會回到下標爲0的位置(利用取餘來實現),即每一次rear和front的更新基於如下操做:rear=(rear+1)%MaxSize,front=(front+1)%MaxSize。
可是這樣又產生了新的問題,rear=front究竟是隊空仍是隊滿沒法判斷。這樣的問題有如下兩種解決辦法:①設一個frag,做爲標誌位,當隊滿時frag=1;②犧牲一個數組位置,保留一個空餘單元,此時隊滿的判斷標準變成了(rear+1)%MaxSize=front,隊列中的元素數計算方法爲:(rear-front+MaxSize)%MaxSize。
循環隊列的入隊操做爲:
bool EnQueue(SqQueue &Q,ElemType x){ if((Q.rear+1)%MaxSize==Q.front) return false; //隊滿 Q.data[Q.rear]=x; Q.rear=(Q.rear+1)%MaxSize; return true; }
循環隊列的出隊操做爲:
bool DeQueue(SqQueue &Q,ElemType &x){ if(Q.rear==Q.front) return false; //隊空 x=Q.data[Q.front]; Q.front=(Q.front+1)%MaxSize; return true; }
隊列鏈式存儲的定義爲:
typedef struct{ ElemType data; //數據域 struct LinkNode *next; //指針域 }LinkNode; //鏈式隊列的結點 typedef struct{ LinkNode *front,*rear; //隊頭和隊尾指針 }LinkQueue;
鏈式隊列的入隊和出隊相似於鏈表的相應操做。