——引子
棧是系統軟件必不可少的數據結構,其應用十分的廣泛,例如:儲存管理、函數調用以及表達式求值等,都離不開棧這個數據結構。
另外,深度優先搜索算法也需要藉助棧來完成。
那麼,棧到底是一種怎樣的結構呢?
棧的特點
棧其實也是線性表中的一種,不過,它是作爲線性表中比較特例的存在。
這種結構,只允許在棧的一端進行操作,這個位置,也被稱爲棧頂!
對於棧的一切操作,例如增、刪、查等,都只能在棧頂進行,對於棧其他位置的元素,沒有影響。
所以,這也就造成了棧最大的特點:後進先出!
對於棧的理解
對於棧,我們可以將其想象成一個瓶子,而元素,就是瓶子中的水,瓶口就相當於是棧頂,瓶底相當於棧底。
當一個元素進入棧時,就像水一樣,直接進入瓶子的最底層,而想要倒水出來,那最先出來的只能是上層的水。
所以,將棧這個結構,當作一個水瓶,是在好不過的了~
還有,關於棧元素的出棧順序,利用的就是我們的卡特蘭數,對於卡特蘭數,在這裏我就不做過多的解釋,想要了解的小夥伴們,可以自己找找相關的書籍~
在爲大家大概的講明棧的邏輯意義之後,就是我們的重頭戲了,下面就讓我們一起,看看棧結構,到底是如何實現的~
棧的實現
既然棧也是一種特別的線性表,那麼在之前的瞭解之中,我們已經知道,線性表分爲兩種,一種是順序的,一種是鏈式的。
那麼,既然棧作爲一個線性表,那肯定也有兩種實現的形式,接下來,就讓我們一起看看,這兩種形式的棧,有何不同之處!
順序棧
使用順序存儲的方式實現棧結構與實現順序表的結構類似,不同的是,對於棧內元素的操作,我們只能從棧頂進行。
由於順序表中,在尾部插入和刪除元素比較容易,所以,在下面的代碼中,我們將表的尾部當作棧頂,具體實現如下:
類型定義
typedef int data;
typedef struct{
int top;
int capacity;
data * array;
}stack;
//這裏利用typedef關鍵字,可以將代碼的可重用性提高一點,想要轉變數據類型時,直接改變就行,數據類型可以時結構體!
初始化
stack * init_stack(stack * p, int d){
p->top = 0;
p->capacity = d?d:8;
p->array = (data *)malloc(sizeof(data) * p->capacity);
return p;
}//初始化棧
銷燬
void destory_stack(stack * p){
free(p->array);
}//銷燬棧
判斷棧是否爲空
_Bool isEmpty_stack(stack * p){
return p->top == 0;
}//判斷棧是否爲空
擴展空間
stack * extend_stack(stack * p){
int i = 0;
data * new_array = (data *)malloc(sizeof(data) * p->capacity * 2);
for(i = 0; i < p->top; i++){
new_array[i] = p->array[i];
}
free(p->array);
p->array = new_array;
return p;
}//空間擴展
入棧
stack * push_stack(stack * p, data d){
if(p->top == p->capacity){
extend_stack(p);
}
p->array[p->top++] = d;
return p;
}//入棧
彈棧
stack * pop_stack(stack * p){
if(p->top == 0){
return (void *)0;
}
p->top--;
return p;
}//彈棧
讀取棧頂元素
stack * read_stack(stack * p, data * da){
if(p->top == 0){
return (void *)0;
}
* da = p->array[p->top - 1];
return p;
}//讀取棧頂元素
測試數據
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
stack s;
init_stack(&s,4);
if(isEmpty_stack(&s)){
printf("棧爲空!\n");
}
printf("開始入棧操作:1,2,3,4,5\n");
push_stack(&s,1);
push_stack(&s,2);
push_stack(&s,3);
push_stack(&s,4);
push_stack(&s,5);
printf("入棧操作完成!\n");
while(!isEmpty_stack(&s)){
data d;
read_stack(&s,&d);
printf("此時棧頂元素是:%d\n",d);
printf("彈棧操作執行!\n");
pop_stack(&s);
}
if(isEmpty_stack(&s)){
printf("棧爲空!\n");
}
destory_stack(&s);
return 0;
}
結果
鏈式棧
使用鏈式存儲的方式實現棧,由於鏈表的頭插和頭刪比較快,所以我們將鏈表的表頭當作是棧頂,具體實現如下:
在這裏插入代碼片
類型定義
typedef int Data;
typedef struct N{
Data data;
struct N * next;
}Node;
typedef struct{
Node * top;
int size;
}Link_stack;
//這裏將數據域類型起別名的原因和上面的順序棧相同,都是爲了代碼可重用!
創建節點
Node * make_node(Data d){
Node * new_node = (Node *) malloc(sizeof(Data));
new_node->data = d;
new_node->next = (void *)0;
return new_node;
}//創建節點
初始化
Link_stack * init_stack(Link_stack * p){
p->size = 0;
p->top = (void *)0;
return p;
}//初始化
銷燬
void destory_stack(Link_stack * p){
while(p->top){
Node * del = p->top;
p->top = p->top->next;
free(del);
}
}//銷燬棧
判斷棧是否爲空
_Bool isEmpty_stack(Link_stack * p){
return p->top == (void *)0;
}//判斷棧是否爲空
入棧
Link_stack * push_stack(Link_stack * p, Data d){
Node * new_node = make_node(d);
if(!new_node){
return (void *)0;
}
new_node->next = p->top;
p->top = new_node;
p->size++;
return p;
}//入棧
彈棧
Link_stack * pop_stack(Link_stack * p){
if(p->size == 0){
return (void *)0;
}
Node * del = p->top;
p->top = p->top->next;
free(del);
p->size--;
return p;
}//彈棧
讀取棧頂元素
Link_stack * read_stack(Link_stack * p, Data * d){
if(p->size == 0){
return (void *)0;
}
*d = p->top->data;
return p;
}//讀取棧頂元素
測試數據
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
int a[5] = {1,2,3,4,5};
int i = 0;
Link_stack s;
init_stack(&s);
if((&s)->size == 0){
printf("棧創建成功!\n");
}
printf("需要入棧的元素有:1,2,3,4,5\n");
for(i = 0; i < 5; i++){
printf("此時入棧的元素是:%d\n",a[i]);
push_stack(&s,a[i]);
if(!isEmpty_stack(&s)){
printf("入棧成功!\n");
}
}
while((&s)->top){
Data d;
printf("此時棧頂元素是:");
read_stack(&s,&d);
printf("%d\n",d);
Link_stack * x = pop_stack(&s);
if(x != (void *)0){
printf("彈棧成功!\n");
}
}
destory_stack(&s);
return 0;
}
結果
總結
這次給大家帶來的就是這麼多了,下一次窩瓜將給大家帶來隊列的簡單實現~