【資料結構】棧的應用---四則運算表示式求值(中綴表示式與字尾表示式轉換)

diligentyang發表於2017-04-29

用計算機實現帶括號的四則運算的方式。

這裡的困難在於乘除運算的優先順序高於加減運算,並且加入了括號,使得問題變得更加困難。

20世紀50年代,波蘭邏輯學家想到了一種不需要括號的字尾表達法,我們也把它稱為逆波蘭表示

比如:9+(3-1)*3+10/2,如果用字尾表示法就是9 3 1 - 3 * + 10 2 / +,這樣的表示式稱為字尾表示式,叫字尾的原因在於所有的符號都是要在運算數字的後面出現。

字尾表示式的計算方式

為了解釋字尾表示式的好處,我們先來看看,計算機是如何計算字尾表示式的。

字尾表示式9 3 1 - 3 * + 10 2 / +

規則:從左到右遍歷表示式的每個數字和符號,遇到數字就進棧,遇到是符號,就將處於棧頂的兩個數字出棧,進行計算,然後計算結果進棧,一直到最終獲得結果。

圖示

圖示

圖示

圖示

果然字尾表示式可以很順利的解決計算問題。那麼這個字尾表示式是怎麼得出來的呢?

中綴表示式轉字尾表示式

我們把平時標準的四則運算表示式比如9+(3-1)*3+10/2 叫做中綴表示式。

中綴表示式:9+(3-1)*3+10/2 轉換字尾表示式: 9 3 1 - 3 * + 10 2 / +

規則:

  1. 噹噹前字元為數字時,直接輸出;
  2. 噹噹前字元為”(“時,將其壓棧;
  3. 噹噹前字元為”)”時,則彈出堆疊中最上的”(“之前的所有運算子並輸出,然後刪除堆疊中的”(” ;
  4. 噹噹前字元為運算子時,則依次彈出堆疊中優先順序大於等於當前運算子的(到”(“之前為止),輸出,再將當前運算子壓棧;
  5. 最後彈出所有棧中的內容輸出

圖示

圖示

圖示

圖示

圖示

程式碼PHP

以下程式碼為本人個人所寫,存在諸多bug,意思點到為止,如有不到之處,敬請指出。

<?php
//將數字項和符號項用空格分隔開
function getItem($str)
{
    $arr = [];
    $num = "";
    for ($i = 0; $i < strlen($str); $i++) {
        //如果是個數字,並且不為最後一項
        if (is_numeric($str[$i]) && $i != strlen($str) - 1) {
            $num .= $str[$i];
        } else {
            //如果不是個數字,並且num不為空,則放入陣列中
            if (!empty($num)) {
                $arr[] = $num;
                $num = "";
            }
            //如果是符號,或者最後一項,直接放入陣列中
            $arr[] = $str[$i];
        }
    }
    return implode(" ", $arr);
}
//將中綴表示式轉換成字尾表示式
function centerToEnd($str_center)
{
    $stack = new \SplStack();
    $arr = explode(" ", $str_center);
    $arr1 = [];
    for ($i = 0; $i < count($arr); $i++) {
        if (is_numeric($arr[$i])) {//如果是個數字,直接放入陣列,輸出
            $arr1[]= $arr[$i];
        }else{
            if($arr[$i]=="("){//如果是左括號,則入棧
                $stack->push($arr[$i]);
            }else if($arr[$i]==")"){//如果是右括號,則彈出堆疊中最上的"("之前的所有運算子並輸出,然後刪除堆疊中的"("
                while(true){
                   $s = $stack->pop();
                   if($s=="("){
                       break;
                   }else{
                       $arr1[]=$s;
                   }
               }
            }else if($arr[$i]=="+"||$arr[$i]=="-"){//噹噹前字元為運算子時,則依次彈出堆疊中優先順序大於等於當前運算子的(到"("之前為止),輸出,再將當前運算子壓棧
                while($stack->count()>0){
                    $s = $stack->pop();
                    if($s=="("){
                        break;
                    }else{
                        $arr1[]=$s;
                    }
                }
            } else if($arr[$i]=="*"||$arr[$i]=="/"){//噹噹前字元為運算子時,則依次彈出堆疊中優先順序大於等於當前運算子的(到"("之前為止),輸出,再將當前運算子壓棧
                while($stack->count()>0){
                    $s = $stack->pop();
                    if($s=="("){
                        break;
                    }else if($s=="+"||$s=="-"){
                        $stack->push($s);
                        break;
                    }else{
                        $arr1[]=$s;
                    }
                }
            }
            if($arr[$i]!==")"){//將當前運算子壓入棧
                $stack->push($arr[$i]);
            }
        }
    }
    //最後彈出棧中的全部內容
    while($stack->count()>0){
        $arr1[]=$stack->pop();
    }
    return implode(" ",$arr1);
}
//計算字尾式的和
function sumEnd($str){
    $arr = explode(" ", $str);
    $stack = new \SplStack();
    for ($i = 0; $i < count($arr); $i++) {
        if (is_numeric($arr[$i])) {//如果是數字,直接壓入棧中
            $stack->push($arr[$i]);
        } else {//如果是符號,則彈出棧頂的兩個數,進行運算,然後將運算結果壓入棧中
            $n2 = $stack->pop();
            $n1 = $stack->pop();
            $stack->push(eval("return " . $n1 . $arr[$i] . $n2 . ";"));
        }
    }
    return $stack->pop();//彈出最後結果
}

$str = "9+(3-1)*3+10/2";
//$str = trim(fgets(STDIN));//標準輸入
$str_center = getItem($str);
//echo $str_center;//9 + ( 3 - 1 ) * 3 + 10 / 2
$str_end = centerToEnd($str_center);
//echo $str_end;//9 3 1 - 3 * + 10 2 / +
echo sumEnd($str_end);



相關文章