1. 程式人生 > >數據結構11: 棧(Stack)的概念和應用及C語言實現

數據結構11: 棧(Stack)的概念和應用及C語言實現

next ret 額外 轉換 lib 順序存儲 順序棧 就是 函數

棧,線性表的一種特殊的存儲結構。與學習過的線性表的不同之處在於棧只能從表的固定一端對數據進行插入和刪除操作,另一端是封死的。

技術分享圖片 圖1 棧結構示意圖 由於棧只有一邊開口存取數據,稱開口的那一端為“棧頂”,封死的那一端為“棧底”(類似於盛水的木桶,從哪進去的最後還得從哪出來)。

棧的“先進後出”原則

使用棧存儲數據元素,對數據元素的“存”和“取”有嚴格的規定:數據按一定的順序存儲到棧中,當需要調取棧中某數據元素時,需要將在該數據元素之後進棧的先出棧,該數據元素才能從棧中提取出來。

如圖 1 ,棧中存放了 4 個數據元素,進棧的順序是 A 先進棧,然後 B 進,然後 C 進,最後 D 進棧;當需要調取 A 時,首先 D 出棧,然後 C 出棧,然後 B 出棧,最後 A 才能出棧被調用。

就好比只有一個門的車庫(每次僅允許一輛車通過),每輛車好比一個數據元素,只有離門最近的車先開出來,裏邊的車才能出來;最裏邊的車是最先開進去的,註定要最後出來。

棧操作數據元素的方法

棧操作數據元素只有兩種動作:
  1. 數據元素用棧的數據結構存儲起來,稱為“入棧”,也叫“壓棧”。
  2. 數據元素由於某種原因需要從棧結構中提取出來,稱為“出棧”,也叫“彈棧”。

棧的兩種表示方式

既然棧也是線性表,那麽它就同樣有線性表的兩種表示形式:順序棧 和 鏈式棧(簡稱“鏈棧”)。
兩者的區別在於存儲的數據元素在物理結構上是否是相互緊挨著的。順序棧存儲元素預先申請連續的存儲單元;鏈棧需要即申請,數據元素不緊挨著。

棧的“上溢”和“下溢”

棧存儲結構調取棧中數據元素時,要避免出現“上溢”和“下溢”的情況:

“上溢”:在棧已經存滿數據元素的情況下,如果繼續向棧內存入數據,棧存儲就會出錯。

“下溢”:在棧內為空的狀態下,如果對棧繼續進行取數據的操作,就會出錯。


棧的“上溢”和“下溢”,可以總結為:棧滿還存會“上溢”,棧空再取會“下溢”。

對於棧的兩種表示方式來說,順序棧兩種情況都有可能發生;而鏈棧由於“隨時需要,隨時申請空間”的存儲結構,不會出現“上溢”的情況。

順序棧

順序棧的實現采用的是數組。

在順序棧中設定一個隨時指向棧頂元素的變量(一般命名為 top ),當 top 的值為 -1 時,說明數組中沒有數據,即棧中沒有數據元素,為“空棧”;只要數據元素進棧,top 就加 1 ;數據元素出棧, top 就減 1 。

例如,使用順序棧的存儲結構將(’a’,’b’,’c’,’d’)四個元素逐個壓棧並輸出棧頂元素。

實現代碼:
#include <stdio.h>
//元素elem進棧 int push(char *a, int top, char elem)
{ a[
++top] = elem; return top; }
//數據元素出棧 int pop(char *a, int top)
{
if (top == -1)
  { printf(
"空棧"); return -1; } printf("彈棧元素:%c\n", a[top]); top--; return top; }
int main()
{
char a[100]; int top = -1; top=push(a, top, a); top=push(a, top, b); top=push(a, top, c); top=push(a, top, d); top=pop(a, top); top=pop(a, top); top=pop(a, top); top=pop(a, top); top=pop(a, top); return 0; }
輸出結果:
彈棧元素:d 彈棧元素:c 彈棧元素:b 彈棧元素:a 空棧

鏈棧

鏈棧,用線性表的鏈式存儲結構實現。

鏈棧一般不需要創建頭結點,頭結點會增加程序的復雜性,只需要創建一個頭指針就可以了。

用鏈表表示棧時,用鏈表頭結點的一端作為棧的棧頂端,這樣做的好處是當數據元素壓棧或者彈棧時,直接使用頭指針就可以完成,不需要增設額外的指針。

例如,用鏈棧實現將(’a’,’b’,’c’,’d’)四個數據元素壓棧,再依次彈棧:

#include <stdio.h>
#include <stdlib.h>
typedef struct lineStack
{
char data; struct lineStack *next; }lineStack;
lineStack
* push(lineStack * stack,char a)
{ lineStack
*line = (lineStack*)malloc(sizeof(lineStack)); line->data = a; line->next = stack; stack = line; return stack; }
lineStack
*pop(lineStack *stack)
{
if (stack)
  { lineStack
*p = stack; stack = stack->next; printf("彈棧元素:%c ", p->data); if (stack)
     { printf(
"棧頂元素:%c\n", stack->data); }
     else
     {   printf("棧已空\n"); } free(p); }
   else
   { printf("棧內沒有元素"); return stack; }
return stack; }
int main()
{ lineStack
*stack = NULL; stack = push(stack, a); stack = push(stack, b); stack = push(stack, c); stack = push(stack, d); stack = pop(stack); stack = pop(stack); stack = pop(stack); stack = pop(stack); stack = pop(stack);
return 0; }
輸出結果:

彈棧元素:d 棧頂元素:c
彈棧元素:c 棧頂元素:b
彈棧元素:b 棧頂元素:a
彈棧元素:a 棧已空
棧內沒有元素

總結

實際生活中使用手機時,屏幕頁面的跳轉使用的就是棧結構(跳轉頁面時,前一個頁面會被存儲到棧中;做回退操作時會回到上一個頁面,這是進棧頁面出棧的效果)。另外在求 n!時,可以通過函數的遞歸來實現,這個過程的底層就用到了棧結構。

除此之外,數制轉換和括號匹配問題也可以用棧來解決(下節介紹)。

數據結構11: 棧(Stack)的概念和應用及C語言實現