測試開發基礎之算法(5):棧的基礎操作及應用

1. 棧的概念

棧是一種“操作受限”的線性表,支持兩種基礎操作,入棧和出棧。特點是先進後出,後進先出,也就說是先入棧的數據後出棧,後入棧的數據先出棧。

棧有幾個概念需要我們瞭解:

  • 棧大小:就是棧的容量,表示最多可以放多少個數據。
  • 棧中元素:棧中的數據
  • 棧頂:棧的最上面的元素
  • 棧底:棧中最下面的元素
  • 入棧:將新的數據放入棧頂
  • 出棧:將數據從棧頂取出來
    在這裏插入圖片描述

2.實現一個棧

棧可以用數組來實現,也可以用鏈表來實現。用數組實現的棧叫順序棧,用鏈表實現的棧叫鏈式棧。棧只支持兩種操作,入棧和出棧。下面用Python的列表來實現一個棧。

class Stack:
    """
    用Python列表實現,其實是一個支持動態擴容的棧。如果是用Java的數組實現的話,需要手動擴容數組。
    入棧和出棧時間複雜度是 O(1)
    入棧和出棧空間複雜度是 O(1)
    """
    def __init__(self):
        self._stack = list()

    def push(self, ele):
        # 不會沒空間,因爲Python的list會動態擴容
        self._stack.append(ele)

    def pop(self):
        if self.is_empty():
            return None
        return self._stack.pop()

    def top(self):
        return self._stack[-1]

    def min(self):
        return min(self._stack)

    def max(self):
        return max(self._stack)

    def is_empty(self):
        return self._stack.__len__() == 0

    def length(self):
        return self._stack.__len__()

    def reverse(self):
        self._stack = self._stack[::-1]

	def clear(self):
        while not self.is_empty():
            self._stack.pop()

    def __repr__(self):
        return str(self._stack)


if __name__ == '__main__':
    s = Stack()
    s.push("a")
    s.push(1)
    s.push(2)
    print(s)
    s.reverse()
    print(s)
    print(s.pop())
    print(s.pop())

3.棧的應用場景

3.1 棧在函數調用中的應用

操作系統給每一個線程分配一塊內存空間,這塊內存被組織成棧這種數據結構,用來存儲函數調用時的臨時變量。每進入一個函數,就會將這個函數的臨時變量放入棧中,當函數執行完成後,再把這些臨時變量從棧中去除。比如下面這塊代碼:

int main() {
   int a = 1; 
   int ret = 0;
   int res = 0;
   ret = add(3, 5);
   res = a + ret;
   printf("%d", res);
   reuturn 0;
}

int add(int x, int y) {
   int sum = 0;
   sum = x + y;
   return sum;
}

a,ret,res這三個是main的臨時變量,x,y,sum是add的臨時變量,main函數調用了add函數。當進入到add函數時,內存的棧數據如下:
在這裏插入圖片描述
當add函數執行完成後,sum,x,y從棧中出棧,當main執行完成後,res,ret,a依次出棧。

3.2 棧在表達式求值中的應用

計算機是如何求表達式3+5*8-6的值的呢?編譯器是採用兩個棧來實現的,一個棧存放數據的操作數棧,一個棧用來存放操作符的運算符棧。

從左到右遍歷表達式,遇到操作數則放入操作數棧。當遇到運算符,就與運算符棧的棧頂元素進行比較。

如果比運算符棧頂的運算符優先級高,則將運算符放入操作符棧。如果比運算符棧頂的運算符優先級低或者相等,則從操作符棧pop出兩個操作數進行運算,再將結果push到操作數棧。繼續比較。
看下3+5*8-6的運算過程:
在這裏插入圖片描述

3.3 用棧判斷括號表達式是否合法

我們常見的括號有(),[],{},在使用這些括號時,括號可以嵌套單必須成對出現,比如{[] ()[{}]}或 [{()}([])] 等都爲合法格式,而{[}()] 或 [({)] 爲不合法的格式。

那如何判斷一個包含三種括號的表達式字符串,它的括號用法是否合法呢?用棧就可以方便的解決這個問題。

從左到右掃描字符串,遇到左括號則放入棧,遇到右括號,則與棧頂元素對比,如果匹配則pop掉棧頂的元素,如果匹配,繼續掃描剩下的字符串。

如果掃描過程中,遇到不能匹配的右括號,或者棧中沒有數據,則字符串不合法。
如果掃描完字符串後,棧爲空,則表示字符串合法。否則,說明有未匹配的左括號,字符串非法。

def match_bracket(strings):
    open_bracket = "({["
    close_bracket = ")]}"
    bracket_map = {'(': ')', '[': ']', '{': '}'}
    label = True
    stack = Stack()  # 創建空棧
    if strings == "":
        return True
    for char in strings: 
        if char not in (open_bracket + close_bracket):  # 只處理左右括號
            continue
        if char in open_bracket:  
            stack.push(char)  # 左括號入棧
            continue
        if char in close_bracket:  
            if stack.is_empty():   # 第一個出現的是右括號
                label = False
                break
            if bracket_map[stack.pop()] == char:   # 出棧
                continue
            else:
                label = False
                break
    if not stack.is_empty():
        label = False
    print(stack)
    return label
if __name__ == '__main__':
    st = "[([{}][])]"
    print(match_bracket(st))

3.4 用棧實現瀏覽器的前進後退功能

瀏覽器的前進、後退功能,大家經常使用。當你依次訪問完一串頁面 a-b-c 之後,點擊瀏覽器的後退按鈕,就可以查看之前瀏覽過的頁面 b 和 a。當你後退到頁面 a,點擊前進按鈕,就可以重新查看頁面 b 和 c。但是,如果你後退到頁面 b 後,點擊了新的頁面 d,那就無法再通過前進、後退功能查看頁面 c 了。
這個功能就可以用棧實現。代碼參考

class Browser:
    def __init__(self):
        self.forward_stack = Stack()  # 前進棧: 保存後退的頁面
        self.backward_stack = Stack()  # 後退棧: 保存新開頁面或者前進的頁面

    def open(self, url):
        self.backward_stack.push(url)  # 新頁面加入到後退棧
        print(f"Open new url: {url}")
        self.forward_stack.clear()  # 清空前進棧

    def back(self):
        if not self.backward_stack.is_empty():
            top = self.backward_stack.pop()  # 從後退棧中彈出
            self.forward_stack.push(top)
            print(f"Back to {top}")
        else:
            print("Cannot backward")

    def forward(self):
        if not self.forward_stack.is_empty():
            top = self.forward_stack.pop()  # 從前進棧中彈出
            self.backward_stack.push(top)
            print(f"Forward to {top}")
        else:
            print("Cannot forward")


if __name__ == '__main__':
    browser = Browser()
    browser.open('a')
    browser.open('b')
    browser.open('c')
    browser.back()
    browser.back()
    browser.open('d')
    browser.back()
    browser.back()
    browser.forward()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章