堆棧

1、什麼是堆棧

計算機如何進行表達式求值?算法

例:算術表達式\(5+6/2-3*4\),正確理解:\(5+6/2-3*4=5+3-3*4=8-3*4=8-12=-4\)數組

  • 由兩類對象構成的:
    • 運算數,\(二、三、4\)
    • 運算符號,\(+、-、*、/\)
  • 不一樣運算符號優先級不同

2、後綴表達式

  • 中綴表達式:運算符號位於兩個運算數之間。如,\(a+b*c-d/e\)
  • 後綴表達式:運算符號位於兩個運算數之間。如,\(abc*+de/-\)

例:\(62/3-42*+=?\)app

後綴表達式求值策略:從左向右「掃描」,逐個處理運算數和運算符號函數

  1. 遇到運算數怎麼辦?如何「記住」目前還不未參與運算的數?
  2. 遇到運算符號怎麼辦?對應的運算數是什麼?

啓示:須要有種存儲方法,能順序存儲運算數,並在須要時「倒序」輸出。性能

例:\(62/3-42*+=?\)spa

時間複雜性能:\(T(N)=O(N)\)3d

3、堆棧的抽象數據類型描述

堆棧(Strack):具備必定操做約束的線性表,只在一端(棧頂,Top)作插入、刪除指針

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

類型名稱:堆棧(Stack)code

數據對象集:一個有0個或多個元素的有窮線性表。

操做集:長度爲\(MaxSize\)的堆棧\(s\in{Stack}\),堆棧元素\(item\in{E}lementType\)

  1. Stack CreateStack(int MaxSize):生成空堆棧,其最大長度爲\(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):刪除並返回棧頂元素;

\(Push\)\(Pop\)能夠穿插交替進行;

按照操做系列

  1. Push(S,A),Push(S,B),Push(S,C),Pop(S),Pop(S,Pop(s)堆棧輸出是:\(CBA\)
  2. Push(S,A),Pop(S),Push(S,B),push(S,C),Pop(S),Pop(S)堆棧輸出是:\(ACB\)

例:若是三個字符按\(ABC\)書序壓入堆棧:\(ABC\)的全部排列不必定都是出棧的序列,沒法產生\(CAB\)這樣的序列。

4、棧的順序存儲實現

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

/* c語言實現 */

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

4.1 入棧

/* c語言實現 */

void Push(Stack PtrS, ElementType item)
{
  if(PtrS->Top == Maxsize-1){
    printf("堆棧滿"); return;
  }else{
    PtrS->Data[++(PtrS->Top)] = item; /* 先+再返回 */
    return;
  }
}

4.2 出棧

/* c語言實現 */

ElementType Pop(Stack PtrS)
{
  if(PtrS->Top == -1){
    printf("堆棧空");,
    return ERROR; /* ERROR是ElementType的特殊值,標誌錯誤 */
  }else
    return (PtrS->Data[(PtrS->Top)--]); /* 先返回再- */
}

5、例:請用一個數組實現兩個堆棧,要求最大地利用數組空間,使數組只要有空間入棧操做就能夠成功。

分析:一種比較聰明的方法是使這兩個棧分別從數組的兩頭開始向中間生長;當兩個棧的棧頂指針相遇時,表示兩個棧都滿了。

/* c語言實現 */

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

5.1 入棧

void Push(struct DStack *PtrS, ElementType item, int Tag)
{ /* Tag做爲區分兩個堆棧的標誌,取值爲1和2 */
  if (PtrS->Top2 - PtrS->Top1 == 1){ /*堆棧滿*/
    printf("堆棧滿"); return;
  }
  if (Tag == 1) /* 對第一個堆棧操做 */
    Ptrs->Data[++(PtrS->Top1)] = item;
  else /*對第二個堆棧操做 */
    Ptrs->Data[--(PtrS->Top2)] = item;
}

5.2 出棧

/* c語言實現 */

ElementType Pop(struct DStack *PtrS, int Tag)
{ /* Tag做爲區分兩個堆棧的標誌,取值爲1和2 */
  if (Tag == 1){ /* 對第一個堆棧操做 */
    if (PtrS->Top1 == -1){ /* 堆棧1空 */
      printf("堆棧1空"); return NULL;
    } else return PtrS->Data[(PtrS->Top1)--];
  } else { /* 對第二個堆棧操做 */
    if (PtrS->Top2 == MaxSize){ /* 堆棧2空 */
      printf("堆棧2空"); return NULL;
    }else return PtrS->Data[(PtrS->Top2)++];
  }
}

6、堆棧的鏈式存儲實現

棧的鏈式存儲結構實際上就是一個單鏈表,叫作鏈棧。插入和刪除操做只能在鏈棧的棧頂進行。棧頂指針Top應該在鏈表的開始;若是棧頂指針放在鏈表尾部,沒法進行出棧操做,由於單鏈表出棧時找不到前一個結點。

/* c語言實現 */

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

6.1 堆棧初始化

/* c語言實現 */

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

6.2 判斷堆棧S是否爲空

/* c語言實現 */

int IsEmpty(Stack S)
{ /* 判斷堆棧S是否爲空,若爲空函數返回整數1,不然返回0 */
  return (s->Nesxt == NULL);
}

6.3 入棧

/* c語言實現 */

void Push(ElementType item, Stack S)
{ /* 將元素item壓入堆棧S */
  struct SNode *TmpCell;
  TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
  TmpCell->ELement = item;
  TmpCell->Next = S->Next;
  S->Next = TmpCell;
}

6.4 出棧

牢記鏈表頭纔是棧頂

/* c語言實現 */

ElementType Pop(Stack S)
{ /* 刪除並返回堆棧S的棧頂元素 */
  struct SNode *FirstCell;
  ElementType TopElem;
  if (IsEmpty(S)){
    printf("堆棧空"); return NULL;
  } else {
    FirstCell = S->Next;
    S->Next = FirstCell->Next;
    TopElem = FirstCell->Element;
    free(FirstCell);
    return TopElem;
  }
}

7、堆棧應用:表達式求值

回憶:應用堆棧實現後綴表達式求值的基本過程:從作到右讀入後綴表達式的各項(運算符或運算數)

  1. 運算數:入棧;

  2. 運算符:從堆棧中彈出適當數量的運算數,計算並結果入棧;
  3. 最後,堆棧頂上的元素就是表達式的結果值。

8、中綴表達式求值

基本策略:將中綴表達式轉換爲後綴表達式,而後求值

如何將中綴表達式轉換爲後綴?

觀察一個簡單例子:\(2+9/3-5\quad->\quad{2\,9\,3/+5-}\)

  1. 運算數相對順序不變
  2. 運算符號順序發生改變
    1. 須要存儲「等待中」的運算符號
    2. 要將當前運算符號與「等待中」的最後一個運算符號比較
  3. 考慮括號的狀況

例:\(a*(b+c)/d=?\quad-->\quad{a}\,b\,c+*d/\)

注意:\((\)的優先級大於*,括號內的\(+\)的優先級大於括號外的*,遇到\()\)應該消除前一個\((\)

時間性能:\(T(N)=O(N)\)

9、中綴表達式如何轉換爲後綴表達式

從頭至尾讀取中綴表達式的每一個對象,對不一樣對象按不一樣的狀況處理。

  1. 運算數:直接輸出;
  2. 左括號:壓入堆棧;
  3. 右括號:棧頂的運算符彈出輸出直到遇到左括號(出棧,不輸出);
  4. 運算符:
    1. 優先級大於棧頂運算符時,則把它壓棧
    2. 優先級小於等於棧頂運算符時,將棧頂運算符彈出輸出;再比較新的棧頂運算符,直到該運算符大於棧頂運算符優先級爲止,而後將該運算符壓棧
  5. 若各對象處理完畢,則把堆棧中存留的運算符一併輸出

10、中綴轉換爲後綴實例

\((2*(9+6/3-5)+4)\)

11、堆棧的其餘應用

  • 函數調用及遞歸實現
  • 深度優先搜索:圖搜索
  • 回溯算法:迷宮算法
  • ......

12、Python實現-堆棧

# python語言實現

class Stack(object):
    def __init__(self):
        self.stack = []

    def push(self, value):  # 進棧
        self.stack.append(value)

    def pop(self):  # 出棧
        if self.stack:
            self.stack.pop()
        else:
            raise LookupError('stack is empty!')

    def is_empty(self):  # 若是棧爲空
        return bool(self.stack)

    def top(self):
        # 取出目前stack中最新的元素
        return self.stack[-1]
相關文章
相關標籤/搜索