1,棧只能從表的一端存取數據,另外一端是封閉的,如圖 1 所示;2,在棧中,不管是存數據仍是取數據,都必須遵循"先進後出"的原則,即最早進棧的元素最後出棧。拿圖 1 的棧來講,從圖中數據的存儲狀態可判斷出,元素 1 是最早進的棧。所以,當須要從棧 中取出元素 1 時,根據"先進後出"的原則,需提早將元素 3 和元素 2 從棧中取出,而後才能成功取出元素 1。html
棧是一種 "特殊" 的線性存儲結構,所以棧的具體實現有如下兩種方式:編程
兩種實現方式的區別,僅限於數據元素在實際物理空間上存放的相對位置,順序棧底層採用的是 數組,鏈棧底層採用的是鏈表。有關順序棧和鏈棧的具體實現會在後續章節中做詳細講解。
{1,2,3,4}
,存儲狀態如
圖 1 所示:
{1,2,3,4}
,其存儲狀態如圖 2 所示:
從數組下標爲 0 的模擬棧存儲數據是經常使用的方法,從其餘數組下標處存儲數據也徹底能夠,這裏只是爲了方便初學者理解。
{1,2,3,4}
的過程。最初,棧是"空棧",即數組是空的,top 值爲初始值 -1,如圖 3 所示:
所以,C 語言實現代碼爲: 數組
//元素elem進棧,a爲數組,top值爲當前棧的棧頂位置 int push(int* a,int top,int elem){ a[++top]=elem; return top; }
代碼中的 a[++top]=elem,等價於先執行 ++top,再執行 a[top]=elem。瀏覽器
注意,圖 6 數組中元素的消失僅是爲了方便初學者學習,其實,這裏只須要對 top 值作 -1 操做便可,由於 top 值自己就表示棧的棧頂位置,所以 top-1 就等同於棧頂元素出棧。而且後期向棧中添加元素時,新元素會存儲在相似元素 4 這樣的舊元素位置上,將舊元素覆蓋。
//數據元素出棧 int pop(int * a,int top){ if (top==-1) { printf("空棧"); return -1; } printf("彈棧元素:%d\n",a[top]); top--; return top; }
代碼中的 if 語句是爲了防止用戶作 "棧中已無數據卻還要數據出棧" 的錯誤操做。代碼中,關於對棧中元素出棧操做的實現,只須要 top 值 -1 便可。編程語言
#include <stdio.h> //元素elem進棧 int push(int* a,int top,int elem){ a[++top]=elem; return top; } //數據元素出棧 int pop(int * a,int top){ if (top==-1) { printf("空棧"); return -1; } printf("彈棧元素:%d\n",a[top]); top--; return top; } int main() { int a[100]; int top=-1; top=push(a, top, 1); top=push(a, top, 2); top=push(a, top, 3); top=push(a, top, 4); top=pop(a, top); top=pop(a, top); top=pop(a, top); top=pop(a, top); top=pop(a, top); return 0; }
程序輸出結果爲:ide
鏈棧的實現思路同順序棧相似,順序棧是將數順序表(數組)的一端做爲棧底,另外一端爲棧頂;鏈棧也如此,一般咱們將鏈表的頭部做爲棧頂,尾部做爲棧底,如圖 1 所示:函數
//鏈表中節點結構 typedef struct lineStack{ int data; struct lineStack* next; }; //壓棧 stack 當前鏈棧 a 入棧元素 lineStack* push(lineStack* stack,int a) { //建立存儲新元素的節點 lineStack* temp = (lineStack*)malloc(sizeof(lineStack)); temp->data = a; //新節點與頭節點創建關聯 temp->next = stack; //更新頭指針指向 stack = temp; return stack; }
例如,圖 2e) 所示的鏈棧中,若要將元素 3 出棧,根據"先進後出"的原則,要先將元素 4 出棧,也就是從鏈表中摘除,而後元素 3 才能出棧,整個操做過程如圖 3 所示:工具
//棧頂元素出棧的實現函數 lineStack* pop(lineStack* stack) { if (stack) { //聲明一個新指針指向棧頂節點 lineStack *p = stack; //更新頭節點 stack = stack->next; printf("出棧元素:%d \n",p->data); if (stack) { printf("新棧頂元素:%d\n",stack->data); } else { printf("棧已空\n"); } free(p); } else { printf("棧內沒有元素\n"); return stack; } return stack;
代碼中經過使用 if 判斷語句,避免了用戶執行"棧已空卻還要數據出棧"錯誤操做。學習
#include<stdlib.h> #include<stdio.h> //鏈表中節點結構 typedef struct lineStack{ int data; struct lineStack* next; }; //壓棧 stack 當前鏈棧 a 入棧元素 lineStack* push(lineStack* stack,int a) { //建立存儲新元素的節點 lineStack* temp = (lineStack*)malloc(sizeof(lineStack)); temp->data = a; //新節點與頭節點創建關聯 temp->next = stack; //更新頭指針指向 stack = temp; return stack; } //棧頂元素出棧的實現函數 lineStack* pop(lineStack* stack) { if (stack) { //聲明一個新指針指向棧頂節點 lineStack *p = stack; //更新頭節點 stack = stack->next; printf("出棧元素:%d ",p->data); if (stack) { printf("新棧頂元素:%d\n",stack->data); } else { printf("棧已空\n"); } free(p); } else { printf("棧內沒有元素\n"); return stack; } return stack; } int main() { lineStack * stack = NULL; stack = push(stack, 1); stack = push(stack, 2); stack = push(stack, 3); stack = push(stack, 4); stack = pop(stack); stack = pop(stack); stack = pop(stack); stack = pop(stack); stack = pop(stack); return 0; }
運行結果:開發工具
一般,稱進數據的一端爲 "隊尾",出數據的一端爲 "隊頭",數據元素進隊列的過程稱爲 "入隊",出隊列的過程稱爲 "出隊"。
棧和隊列不要混淆,棧結構是一端封口,特色是"先進後出";而隊列的兩端全是開口,特色是"先進先出"。
{1,2,3,4}
用順序隊列存儲的實現操做如圖 2 所示:
#include <stdio.h> int enQueue(int *a,int rear,int data){ a[rear]=data; rear++; return rear; } void deQueue(int *a,int front,int rear){ //若是 front==rear,表示隊列爲空 while (front!=rear) { printf("出隊元素:%d\n",a[front]); front++; } } int main() { int a[100]; int front,rear; //設置隊頭指針和隊尾指針,當隊列中沒有元素時,隊頭和隊尾指向同一塊地址 front=rear=0; //入隊 rear=enQueue(a, rear, 1); rear=enQueue(a, rear, 2); rear=enQueue(a, rear, 3); rear=enQueue(a, rear, 4); //出隊 deQueue(a, front, rear); return 0; }
程序輸出結果:
出隊元素:1
出隊元素:2
出隊元素:3
出隊元素:4
#include <stdio.h> #define max 5//表示順序表申請的空間大小 int enQueue(int *a,int front,int rear,int data){ //添加判斷語句,若是rear超過max,則直接將其從a[0]從新開始存儲,若是rear+1和front重合,則表示數組已滿 if ((rear+1)%max==front) { printf("空間已滿"); return rear; } a[rear%max]=data; rear++; return rear; } int deQueue(int *a,int front,int rear){ //若是front==rear,表示隊列爲空 if(front==rear%max) { printf("隊列爲空"); return front; } printf("%d ",a[front]); //front再也不直接 +1,而是+1後同max進行比較,若是=max,則直接跳轉到 a[0] front=(front+1)%max; return front; } int main() { int a[max]; int front,rear; //設置隊頭指針和隊尾指針,當隊列中沒有元素時,隊頭和隊尾指向同一塊地址 front=rear=0; //入隊 rear=enQueue(a,front,rear, 1); rear=enQueue(a,front,rear, 2); rear=enQueue(a,front,rear, 3); rear=enQueue(a,front,rear, 4); //出隊 front=deQueue(a, front, rear); //再入隊 rear=enQueue(a,front,rear, 5); //再出隊 front=deQueue(a, front, rear); //再入隊 rear=enQueue(a,front,rear, 6); //再出隊 front=deQueue(a, front, rear); front=deQueue(a, front, rear); front=deQueue(a, front, rear); front=deQueue(a, front, rear); return 0; }
程序運行結果:
1 2 3 4 5 6
在建立鏈式隊列時,強烈建議初學者建立一個帶有頭節點的鏈表,這樣實現鏈式隊列會更簡單。
//鏈表中的節點結構 typedef struct QNode{ int data; struct QNode * next; }QNode; //建立鏈式隊列的函數 QNode * initQueue(){ //建立一個頭節點 QNode * queue=(QNode*)malloc(sizeof(QNode)); //對頭節點進行初始化 queue->next=NULL; return queue; }
鏈隊隊列中,當有新的數據元素入隊,只需進行如下 3 步操做:
由此,新節點就入隊成功了。
例如,在圖 1 的基礎上,咱們依次將 {1,2,3}
依次入隊,各個數據元素入隊的過程如圖 2 所示:
QNode* enQueue(QNode * rear,int data){ //一、用節點包裹入隊元素 QNode * enElem=(QNode*)malloc(sizeof(QNode)); enElem->data=data; enElem->next=NULL; //二、新節點與rear節點創建邏輯關係 rear->next=enElem; //三、rear指向新節點 rear=enElem; //返回新的rear,爲後續新元素入隊作準備 return rear; }
當鏈式隊列中,有數據元素須要出隊時,按照 "先進先出" 的原則,只需將存儲該數據的節點以及它以前入隊的元素節點按照原則依次出隊便可。這裏,咱們先學習如何將隊頭元素出隊。
鏈式隊列中隊頭元素出隊,須要作如下 3 步操做:
例如,在圖 2b) 的基礎上,咱們將元素 1 和 2 出隊,則操做過程如圖 3 所示
void DeQueue(QNode * top,QNode * rear){ if (top->next==NULL) { printf("隊列爲空"); return ; } // 一、 QNode * p=top->next; printf("%d",p->data); top->next=p->next; if (rear==p) { rear=top; } free(p); }
注意,將隊頭元素作出隊操做時,需提早判斷隊列中是否還有元素,若是沒有,要提示用戶沒法作出隊操做,保證程序的健壯性。
經過學習鏈式隊列最基本的數據入隊和出隊操做,咱們能夠就實際問題,對以上代碼作適當的修改。
前面在學習順序隊列時,因爲順序表的侷限性,咱們在順序隊列中實現數據入隊和出隊的基礎上,又對實現代碼作了改進,令其可以充分利用數組中的空間。鏈式隊列就不須要考慮空間利用的問題,由於鏈式隊列自己就是實時申請空間。所以,這能夠算做是鏈式隊列相比順序隊列的一個優點。
這裏給出鏈式隊列入隊和出隊的完整 C 語言代碼爲:
#include <stdio.h> #include <stdlib.h> typedef struct QNode{ int data; struct QNode * next; }QNode; QNode * initQueue(){ QNode * queue=(QNode*)malloc(sizeof(QNode)); queue->next=NULL; return queue; } QNode* enQueue(QNode * rear,int data){ QNode * enElem=(QNode*)malloc(sizeof(QNode)); enElem->data=data; enElem->next=NULL; //使用尾插法向鏈隊列中添加數據元素 rear->next=enElem; rear=enElem; return rear; } QNode* DeQueue(QNode * top,QNode * rear){ if (top->next==NULL) { printf("\n隊列爲空"); return rear; } QNode * p=top->next; printf("%d ",p->data); top->next=p->next; if (rear==p) { rear=top; } free(p); return rear; } int main() { QNode * queue,*top,*rear; queue=top=rear=initQueue();//建立頭結點 //向鏈隊列中添加結點,使用尾插法添加的同時,隊尾指針須要指向鏈表的最後一個元素 rear=enQueue(rear, 1); rear=enQueue(rear, 2); rear=enQueue(rear, 3); rear=enQueue(rear, 4); //入隊完成,全部數據元素開始出隊列 rear=DeQueue(top, rear); rear=DeQueue(top, rear); rear=DeQueue(top, rear); rear=DeQueue(top, rear); rear=DeQueue(top, rear); return 0; }
程序運行結果爲:
1 2 3 4
隊列爲空