堆棧與隊列的實際應用
堆棧和隊列是最基本的兩個ADT,簡單但是重要。先講堆棧在計算機中的應用。
堆棧:
1.用於符號匹配。
在編譯器的語法檢查中,一個過程就是檢查各種括號是否匹配,比如 ([]) ,這就是匹配的,而 {[}] 就不匹配了。可以用堆棧來實現括號匹配。
具體算法如下:
建立一個空的堆棧。
while( 文件沒有結束 ) {
讀取一個字符。
if 遇到一個左括號,把它入棧。
else if 遇到右括號 then 檢查堆棧,{
if 堆棧為空 then 報告錯誤,終止程序(括號不匹配)。
else if 堆棧非空 then {
if 棧頂不是對應的左括號 then 報錯,終止程序。
彈出棧頂。
}
}
if 棧非空 then 報錯。
2.用於計算代數式。( 也可以用二叉樹來解決 )
如果我們要計算 6 + 4 * 8 ,要考慮到優先級的問題,這時候就可以用到堆棧了。
先要把代數式構造成 6 4 8 * + (構造方法也是用堆棧,在下一條會講到)。逐個讀取數據,當讀到數字時, 把數字入棧,
讀到運算符時,彈出棧中的兩個元素(因為這里的是二元運算符,所以彈出兩個,如果是sin等一元運算符就彈出一個),根據讀取
的運算符執行運算,把結果壓入棧中,然后繼續讀取數據,讀取結束后棧頂元素就是結果。
比如讀取6,4,8,由於是數字,所以依次入棧,
讀到 "*" 時,彈出 4 和 8,相乘得到 32,把32入棧。讀到 "+" 時,
彈出 6 和 32 ,執行運算得到 38,壓入棧中,接着讀取結束,棧頂的 38 就是結果。
3.構造表達式。( 也可以用二叉樹來解決 )
比如一個正常的代數式(叫他infix), a + b * c + ( d * e + f ) * g , 轉化成表達式 a b c * + d e * f + g * +, 這個表達式我們叫他 postfix。
把postfix按照 2 中的算法計算就能得到正確的計算順序。
先規定優先級,加減的優先級最低,左括號優先級最高
創建一個字符串output儲存 postfix。
創建一個空棧 operators。
while ( )
逐個讀取 infix 中的元素,儲存在 temp 中。
if temp 是數字(operand)then 把 temp 壓入 output 字符串。
if temp 是除了右括號 ")" 之外的運算符(operator)
if operators 棧空 then temp 入棧。
if operators 棧非空,
while ( 棧頂元素優先級大於或等於 temp 並且 棧頂元素不等於左括號 )
彈出棧頂元素到 output 。
if temp 是右括號 ")"
while ( 棧頂元素不等於左括號 )
彈出棧頂元素到 ouput.
彈出左括號, 但是不輸出到 output
比如 a * ( b * c + d ),左邊是堆棧中的情況,右邊是輸出
1. 輸出 a,把 "*" 入棧
operators output
top * a
2. "(" 入棧,輸出 b
operators output
top (
* a b
3. "*"入棧,輸出 c
operators output
top * a b c
(
*
4. 讀到 + 號,因為棧頂的 * 優先級大於 + 號,所以彈出棧頂到 output,也就是彈出 "*" ,並輸出 "*"
operators output
top + a b c * d
(
*
5.讀到 ")" 右括號,彈出左括號 "(" 上的所有運算符,並彈出左括號 "(" ,注意此時左括號沒有輸出
operators output
top * a b c * d +
6.最后所有元素依次出棧
operators output
top a b c * d + *
4.用於函數調用
因為CPU一次只能執行一個命令,而寄存器也是公用的,
當前函數 current() 在運行時,數據儲存在寄存器中,如果要調用另外一個函數 target(),而target() 也要求使用寄存器,為了防止數據丟失並且在執行完 target()
能夠返回到 current() 繼續執行, 這時候就要把當前函數的重要數據儲存起來,壓入內存中的棧中( 包括變量的值和函數地址 )。這樣target()函數就可以無所顧忌的使用寄存器了。
target() 函數執行結束就取棧頂的返回地址繼續執行 current()。如果target()中又調用另外一個函數,相應的操作也是一樣的。
這種機制和括號匹配有點相似,函數調用就像遇到了一個左括號,函數返回就像遇到一個右括號。
這種機制就是遞歸的原理。
遞歸返回地址就是自己。
(
這句話可能有問題,我就是這么理解的)。
棧的空間有限,如果遞歸沒有結束條件,就會不斷的壓棧,然后棧溢出,程序出錯。
隊列:
當多個任務分配給打印機時,為了防止沖突,創建一個隊列,把任務入隊,按先入先出的原則處理任務。
當多個用戶要訪問遠程服務端的文件時,也用到隊列,滿足先來先服務的原則。
……
有一個數學的分支,叫隊列理論(queuing theory )。用來計算 預測用戶在隊中的等待時間,隊的長度等等問題。
答案取決於用戶到達隊列的頻率,用戶的任務的處理時間。如果比較簡單的情況,可以單單用分析解決。一個簡單的例子就是,
一部電話,一個接線員。當一個電話打進來,如果接線員很忙,就電話放到等待線路后。接線員從隊頭開始應答。
如果有k個接線員,問題就變的復雜了。通常難以分析解決的問題就用模擬來解決。 可以用一個隊列來模擬這個問題。如果k的值很大,
我們也可能需要其他數據結構來模擬問題。通過模擬,可以找到最小的 k 值,使隊列滿足一個合理的等待時間。