數據結構——棧的詳解

棧和隊列是兩種重要的線性結構,從數據結構的角度看,棧和隊列也是線性表,其特殊性在於棧和隊列的基本操作是線性表的子集。他們是操作受限的線性表,因此,可稱爲限定性的數據結構。但從數據類型角度看,他們是和線性表大不相同的兩類重要的的抽象數據類型。

C語言中的棧

棧的定義

棧(stack)是限定僅在表尾進行插入或者刪除的線性表。對於棧來說,表尾端稱爲棧頂(top),表頭端稱爲棧低(bottom)。不含元素的空表稱爲空棧。因爲棧限定在表尾進行插入或者刪除,所以棧又被稱爲後進先出的線性表(簡稱LIFO:Last in, First out.結構)。
棧的示意圖

C語言中棧的基本操作

棧的基本操作主要有:棧的初始化、判空、判滿、取棧頂元素、在棧頂進行插入和刪除。在棧頂插入元素稱爲入棧,在棧頂刪除元素稱爲出棧

棧的初始化

棧和線性表類似,也有兩種存儲表示方法順序棧鏈棧,鏈棧的操作是線性表操作的特例,操作比較容易實現。順序棧即棧的順序存儲結構是利用一組地址連續的存儲單元依次存放自棧底到棧頂的數據元素,同時附設指針top指示棧頂元素在順序棧中的位置,top = 0表示空棧。由於棧在使用的過程中所需要的大小難以估計,所以通常是先爲棧分配一個基本容量,然後再使用的過程中,當棧的空間不夠使用的時候再繼續追加存儲空間。我們以下述類型說明作爲順序棧的定義:

typedef struct{
	SDataType *base; //棧底指針
	SDataType *top;  //棧頂指針
	int StackSize;   //當前已經分配的存儲空間,以元素爲單位 
}SqStack;

棧的初始化操作爲:按照設定的初始分配量進行第一次存儲分配,這裏使用==malloc()==函數來分配存儲空間。malloc()函數的詳細說明請看:malloc詳細說明。base作爲棧底指針,它始終指向棧底,所以s.top = s.base可以作爲棧空的標記。top爲棧頂指針,top的初值指向棧底。每當插入一個元素時top加1,彈出一個元素時top減1,因此,非空棧中的棧頂指針始終在棧頂元素的下一個位置上
棧頂指針和棧中元素的關係圖

//初始化順序棧,構造一個空棧
Status InitStack(SqStack &S){
	//分配存儲空間 
	S.base = (SDataType *)malloc(STACK_INIT_SIZE*sizeof(SDataType));
	if(!S.base){
		//如果分配失敗,則返回error 
		return OVERFLOW;
	}
	//S.top 始終指向棧頂元素的下一個位置 
	S.top = S.base;    //初始狀態下爲空棧 
	S.StackSize = STACK_INIT_SIZE;   //當前已經分配的存儲容量爲100個 
	return OK;	
}

判斷是否爲空棧

當我們彈出棧頂元素時,往往需要判斷一下棧是否爲空來防止發生下溢。上面我們說到==base作爲棧底指針,它始終指向棧底,所以s.top = s.base可以作爲棧空的標記。==所以我們可以這樣判斷棧是否爲空:

//判斷是否爲空棧
void judgeNull(SqStack &s){
	if(s.top == s.base){
		printf("此棧爲空棧!\n");
	}else{
		printf("此棧不爲空棧!\n");
	}
}

判斷是否爲滿棧

當我們使一個元素入棧的之前,我們往往需要判斷一下棧是否爲滿棧,防止發生上溢的情況。因爲我們定義了一個StackSize來表示當前已經分配的存儲空間,所以我們可以用s.top - s.base 來算出當前已經使用的棧空間。所以當s.top - s.base == s.StackSize時表示已經滿棧:

//判斷是否爲滿棧
void judgeFull(SqStack &s){
	if(s.top-s.base == s.StackSize){
		printf("棧滿!\n");
	}else{
		printf("棧未滿!\n");
	} 
}

入棧

入棧時我們首先要判斷棧是否爲滿棧,如果爲滿棧我們要首先追加存儲空間,然後才能將元素入棧。realloc()函數詳解請看realloc詳解

//入棧
Status Push(SqStack &s,SDataType e){
	SDataType *p;
	//首先判斷棧是不是滿的(上溢) 
	if(s.top-s.base == s.StackSize){
		//追加空間 
		p = (SDataType *)realloc(s.base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SDataType));
		if(!p){
			//如果沒有找到符合條件的存儲空間,則返回error 
			return OVERFLOW;
		}
		//成功找到則使s.base指向p 
		s.base = p;
		s.top = s.base + s.StackSize;
		s.StackSize +=  STACKINCREMENT;
	}
	//先插入元素,然後將棧頂指針加 1 
	*(s.top) = e;
	s.top++;
	return OK;
}

出棧

出棧時我們首先要判斷棧是否爲空棧。如果棧已經空了,則返回error。

//出棧
Status Pop(SqStack &s,SDataType &e){
	//判斷是否會發生下溢 
	if(s.top != s.base){
		s.top--;    //先將棧頂指針減 1 
		e = *(s.top);
	}else{
		return 0;
	}
	return e;
}

C語言實現棧的具體代碼

#include<stdio.h>
#include<malloc.h>

#define STACK_INIT_SIZE 100  //棧的初始容量 
#define STACKINCREMENT 10    //容量增量
#define OK 1 
#define OVERFLOW -2
typedef int SDataType;
typedef int Status;

typedef struct{
	SDataType *base; //棧底指針
	SDataType *top;  //棧頂指針
	int StackSize;   //當前已經分配的存儲空間,以元素爲單位 
}SqStack;

//初始化順序棧,構造一個空棧
Status InitStack(SqStack &S){
	//分配存儲空間 
	S.base = (SDataType *)malloc(STACK_INIT_SIZE*sizeof(SDataType));
	if(!S.base){
		//如果分配失敗,則返回error 
		return OVERFLOW;
	}
	//S.top 始終指向棧頂元素的下一個位置 
	S.top = S.base;    //初始狀態下爲空棧 
	S.StackSize = STACK_INIT_SIZE;   //當前已經分配的存儲容量爲100個 
	return OK;	
} 

//入棧
Status Push(SqStack &s,SDataType e){
	SDataType *p;
	//首先判斷棧是不是滿的(上溢) 
	if(s.top-s.base == s.StackSize){
		//追加空間 
		p = (SDataType *)realloc(s.base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SDataType));
		if(!p){
			//如果沒有找到符合條件的存儲空間,則返回error 
			return OVERFLOW;
		}
		//成功找到則使s.base指向p 
		s.base = p;  //系統會將原來的內容複製過來
		s.top = s.base + s.StackSize;
		s.StackSize +=  STACKINCREMENT;
	}
	//先插入元素,然後使棧頂指針加 1 
	*(s.top) = e;
	s.top++;
	return OK;
} 

//出棧
Status Pop(SqStack &s,SDataType &e){
	//判斷是否會發生下溢 
	if(s.top != s.base){
		s.top--;    //先將棧頂指針減 1 
		e = *(s.top);
	}else{
		return 0;
	}
	return e;
}

//判斷是否爲空棧 
void judgeNull(SqStack &s){
	if(s.top == s.base){
		printf("此棧爲空棧!\n");
	}else{
		printf("此棧不爲空棧!\n");
	}
}

//判斷是否爲滿棧
void judgeFull(SqStack &s){
	if(s.top-s.base == s.StackSize){
		printf("棧滿!\n");
	}else{
		printf("棧未滿!\n");
	} 
} 

int main(){
	SqStack s;
	SDataType element;
	
	InitStack(s);  //初始化棧
	//將1-5入棧
	for(int i=1;i<=10;i++){
		Push(s,i);
	}
	
	judgeNull(s);
	judgeFull(s);
	
	printf("出棧:\n");
	//只要棧不爲空 
	while(s.top != s.base){
		Pop(s,element);    //出棧的元素用e接收 
		printf("%d ",element);
	}
	
	printf("\n"); 
	judgeNull(s);
	
	return 0;
	 
} 

C++中的棧

C++ 對模板(Template)支持得很好,STL 就是藉助模板把常用的數據結構及其算法都實現了一遍,並且做到了數據結構和算法的分離。STL的代碼從廣義上講分爲三類:algorithm(算法)、container(容器)和iterator(迭代器),幾乎所有的代碼都採用了模板類和模版函數的方式,這相比於傳統的由函數和類組成的庫來說提供了更好的代碼重用機會。在C++標準中,STL被組織爲下面的13個頭文件:<algorithm >、<deque >、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>。其中的<stack>就是棧。

C++的STL已經將棧的操作都封裝成了函數,我們只需要引進#include<stack>頭文件即可使用。

C++中棧的基本操作

初始化

我們可以直接使用stack<int> s;來創建一個空的 stack 對象。

判斷是否爲空棧

使用empty()函數來判斷棧是否爲空。
empty()函數詳解

入棧

使用push()函數來完成入棧操作。
push()函數詳解

出棧

使用pop()函數實現出棧
pop()函數詳解

返回棧頂元素

使用top()函數返回棧頂元素
top()函數詳解

返回棧中元素數目

使用size()函數返回棧中元素的數目。
size()函數詳解


以上就是C語言和C++中棧的基本用法了,如果你覺得我的文章對你有用請點個贊支持一下吧,如果喜歡我寫的文章那麼請點個關注再走。
嘿嘿
下一篇將繼續寫數據結構的隊列,後續將會再寫一些有關棧和隊列的具體應用。我是ACfun:一個成長中的程序猿,感謝大家的支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章