演算法手記(2)Dijkstra雙棧算術表示式求值演算法

weixin_34321977發表於2014-09-15

這兩天看到的內容是關於棧和佇列,在棧的模組發現了Dijkstra雙棧算術表示式求值演算法,可以用來實現計算器型別的app。

程式語言系統一般都內建了對算術表示式的處理,但是他們是如何在內部實現的呢?為了瞭解這個過程,我們可以自行搭建一套簡易的算術表示式處理機制,這裡就用到棧特性和本篇提到的Dijkstra演算法。

 

概述:
     算術表示式可能是一個數、或者是由一個左括號、一個算術表示式、一個運算子、另一個算術表示式和一個右括號組成的表示式。為了簡化問題,這裡定義的是未省略括號的算術表示式,它明確地說明了所有運算子的運算元,形式如下:

                                                                  (1+((2+3)*(4*5)))

 

思路:

     表示式由括號、運算子和運算元構成,我們根據以下4中情況從左至右逐個將這些實體送入棧處理:

     1.將運算元壓入運算元棧;

     2.將運算子壓入運算子棧;

     3.忽略左括號;

     4.在遇到右括號時,彈出一個運算子,彈出所需數量的運算元,並將運算後的結果壓入運算元棧;

    在處理完最後一個右括號時,運算元棧上只會剩下一個值,它就是表示式的計算結果。這種方法咋一看難理解,但要證明它能計算得到正確的值很簡單:

    每當演算法遇到一個括號包圍,並由一個運算子和兩個運算元組成的子式時,他都將運算子和運算元運算結果壓入運算元棧。這樣的結果就像是在輸入中用這個值代替了該子表示式,因此用這個值代替子表示式得到的結果和原表示式相同。我們可以反覆應用這個規律並得到一個最終值。

例如:

(1+((2+3)*(4*5)))

(1+(5*(4*5)))

(1+(5*20))

(1+100)

  101

 

程式碼實現:


這裡我採用C#來實現,最終執行效果完全符合預期,證明了此演算法的正確性,程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Evaluate
{
    class Program
    {
        static void Main(string[] args)
        {
            string testExpress = "(1+((2+3)*(4*5)))";
            Console.WriteLine(Evaluate(testExpress));
        }

        //DijkStra
        static double Evaluate(string express)
        {
            var expressChars = express.ToArray<char>();
            Stack<char> ops = new Stack<char>();
            Stack<double> vals = new Stack<double>();
            if (express.Length > 0)
            {
                foreach (var opt in expressChars)
                {
                    switch (opt)
                    {
                        case '+':
                        case '-':
                        case '*':
                        case '/':
                            ops.Push(opt);
                            break;
                        case ')':
                            var op = ops.Pop();
                            var v = vals.Pop();
                            switch (op)
                            {
                                case '+':
                                    v += vals.Pop();
                                    break;
                                case '-':
                                    v = vals.Pop() - v;
                                    break;
                                case '*':
                                    v *= vals.Pop();
                                    break;
                                case '/':
                                    v = vals.Pop() / v;
                                    break;
                            }
                            vals.Push(v);
                            break;
                        case ' ':
                        case '(':
                            break;
                        default:
                            vals.Push(double.Parse(opt.ToString()));
                            break;
                    }
                }
                return vals.Pop();

            }
            return double.MaxValue;
        }
    }
}

 

總結:

Dijkstra演算法充分利用了棧的特性,具備較高的執行效率,經過進一步的擴充修改,就完全可以實現具備科學計算功能的複雜計算類app。如果大家還有更好的,更適用的演算法,歡迎在評論中給出地址,謝謝。

相關文章