2. 線性結構--棧

定義

具有一定操作約束的線性表,只在一端(棧頂、Top)做插入、刪除

  • 插入數據:入棧(Push)
  • 刪除數據:出棧(Pop)
  • 後入先出:Last In First Out(LIFO)

抽象數據類型描述

  • 類型名稱:棧(Stack)
  • 數據對象集:一個有0個或多個元素的有窮線性表
  • 操作集:長度爲MaxSize的棧SStack ,堆棧元素itemElementType
    1. Stack CreateStack(int MaxSize):生成空棧,其3最大長度爲MaxSize
    2. int IsFull(Stack S, int MaxSize):判斷棧S是否已滿
    3. void Push(Stack S, ElementType item):將元素item壓入棧
    4. int IsEmpty(Stack S):判斷棧S是否爲空
    5. ElementType Pop(Stack S):刪除並返回棧頂元素

順序存儲實現

棧的順序存儲結構通常由一個一維數組和一個記錄棧頂元素位置的變量組成

代碼定義

#define MaxSize <存儲數據元素的最大個數>
typedef struct SNode *Stack;
struct SNode {
    ElementType Data[MaxSize];
    int Top;
};

主要操作

入棧

void Push(Stack PtrS, ElementType item) {
    if (PtrS->Top == MaxSize - 1) {
        printf("堆棧滿");
        return;
    }

    PtrS->Data[++(PtrS->Top)] = item;
}

出棧

ElementType Pop(Stack PtrS) {
    if (PtrS->Top == -1) {
        printf("堆棧空");
        return ERROR;           // ERROR是ElementType的特殊值,標誌錯誤
    }

    return PtrS->Data[(PtrS->Top)--];
}

一個數組實現兩個棧

使用一個數組實現兩個棧,要求最大地利用數組空間,使數組只要有空間入棧操作就可以成功

分析

使這兩個棧分別從數組的兩頭開始向中間生長,當兩個棧的棧頂指針相遇,表示兩個棧都滿了。

結構定義

#define MaxSize <存儲數據元素的最大個數>
struct DStack {
    ElementType Data[MaxSize];
    int Top1;       // 棧1的棧頂指針
    int Top2;       // 棧2的棧頂指針
} S;
S.Top1 = -1;
S.Top2 = MaxSize;

主要操作

入棧
// Tag 作爲區分兩個棧的標誌,取值爲1和2
void Push(struct DStack *PtrS, ElementType item, int Tag) {
    if (PtrS->Top2 - PtrS->Top1 == 1) {     // 棧滿
        printf("棧滿");
        return;
    }

    if (Tag == 1)   // 對第一個棧操作
        PtrS->Data[++(PtrS->Top1)] = item;
    else            // 對第二個棧操作
        PtrS->Data[--(PtrS->Top2)] = item;
}

出棧

// Tag作爲區分兩個棧的標誌,取值爲1和2
ElementType Pop(struct DStack *PtrS, int Tag) {
    if (Tag == 1) {
        if (PtrS->Top1 == -1) {
            printf("堆棧1空");
            return NULL;
        }

        return PtrS->Data[(PtrS->Top1)--];
    } else {
        if (PtrS->Top2 == MaxSize) {
            printf("堆棧2空");
            return NULL;
        }

        return PtrS->Data[(PtrS->Top2)++];
    }

}

鏈式存儲實現

棧的鏈式存儲結構實際上就是一個單鏈表,叫做鏈棧。插入和刪除操作只能在鏈棧的棧頂進行。

棧頂指針Top應該在鏈表的頭部。

代碼定義

typedef struct SNode *Stack;
struct SNode {
    ElementType Data;
    struct SNode *Next;
};

主要操作

棧初始化(建立空棧)

// 構建一個棧的頭結點,返回指針
Stack CreateStack() {
    Stack S;
    S = (Stack)malloc(sizeof(struct SNode));
    S->Next = NULL;
    return S;
}

判斷棧是否爲空

// 判斷堆棧S是否爲空,如果爲空返回1,否則返回0
int IsEmpty(Stack S) {
    return (S->Next == NULL);
}

入棧

void Push(ElementType item, Stack S) {
    struct SNode *TmpCell;
    TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
    TmpCell->Data = item;
    TmpCell->Next = S->Next;
    S->Next = TmpCell;
}

出棧

ElementType Pop(Stack S) {
    struct SNode *FirstCell;
    ElementType TopElem;
    if (IsEmpty(S)) {
        printf("棧空");
        return NULL;
    }

    FirstCell = S->Next;
    S->Next = FirstCell->Next;
    TopElem = FirstCell->Data;
    free(FirstCell);

    return TopElem;
}

棧的應用:表達式求值

應用棧實現後綴表達式求值

從左到右讀入後綴表達式的各項(運算符或運算數):
1. 運算數:入棧
2. 運算符:從棧中彈出適當數量的運算數,計算並把結果入棧
3. 最後,棧頂上元素就是表達式的結果值

中綴表達式求值

基本策略

將中綴表達式轉換爲後綴表達式,然後求值

轉換過程

從頭到尾讀取中綴表達式的每個對象,對不同對象按不同的情況處理:
1. 運算數:直接輸出
2. 左括號:壓入棧
3. 右括號:將棧頂的運算符彈出並輸出,直到遇到左括號(左括號出棧,但是不輸出)
4. 運算符:
* 若優先級大於棧頂運算符時,則把它壓棧
* 若優先級小於等於棧頂運算符時,將棧頂運算符彈出並輸出;再比教新的棧頂運算符,直到該運算符大於棧頂運算符優先級爲止,然後將該運算符壓棧
5. 若各對象處理完畢,則把棧中存留的運算符一併輸出

中綴轉後綴示例:(2(9+6/35)+4)

步驟 待處理表達式 堆棧狀態(底<–>頂) 輸出狀態
1 2(9+6/35)+4
2 (9+6/35)+4 2
3 (9+6/35)+4 2
4 9+6/35)+4 ( 2
5 +6/35)+4 ( 2 9
6 6/35)+4 ( + 2 9
7 /35)+4 ( + 2 9 6
8 35)+4 ( + / 2 9 6
9 5)+4 ( + / 2 9 6 3
10 5)+4 ( 2 9 6 3 / +
11 )+4 ( 2 9 6 3 / + 5
12 +4 2 9 6 3 / + 5
13 4 + 2 9 6 3 / + 5
14 + 2 9 6 3 / + 5 4
15 2 9 6 3 / + 5 4 +
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章