c# 實現逆波蘭式


運用大眾例子:

所謂的逆波蘭表示法Reverse Polish notationRPN,或逆波蘭記法),是一種數學表達式方式,在逆波蘭記法中,所有操作符置於操作數的后面,因此也被稱為后綴表示法。逆波蘭記法不需要括號來標識操作符的優先級。(摘自維基)
我們平時長寫的數學公式被稱為中綴表達式,即:(a+b)*c,用逆波蘭式為ab+c*,再如:(a+b)*c-(a+b)/e,逆波蘭式為ab+c*ab+e/-,首先理解下轉化成逆波蘭式的幾個步驟:

算法實現(選自百度):

首先需要分配2個棧,一個作為臨時存儲運算符的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:

(1)若取出的字符是 操作數,則分析出完整的運算數,該操作數直接送入S2棧
(2)若取出的字符是 運算符,則將該運算符與S1棧棧頂元素比較,如果該 運算符優先級大於S1棧棧頂運算符優先級,則將該運算符進S1棧,否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,最后將該運算符送入S1棧。
(3)若取出的字符是“(”,則直接送入S1棧頂。
(4)若取出的字符是“)”,則將距離S1棧棧頂最近的“(”之間的運算符,逐個 出棧,依次送入S2棧,此時拋棄“(”。
(5)重復上面的1~4步,直至處理完所有的輸入字符
(6)若取出的字符是“#”,則將S1棧內所有運算符(不包括“#”),逐個出棧,依次送入S2棧。
最后出來的S2需要逆序處理下,便是逆波蘭表達式了。
看着定義,然后粗略了實現了下面的代碼:
主方法:
static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("輸入計算公式:");
                //輸入公式
                string formula = Console.ReadLine();
                //定義臨時計算符棧
                Stack<string> opStack = new Stack<string>();
                opStack.Push("#");
                Stack<string> numStack = new Stack<string>();
                for (int i = 0; i < formula.Length;)
                {
                    int opNum = GetOperationLevel(formula[i].ToString());
                    if (opNum == 0)
                    {
                        int index = GetCompleteValue(formula.Substring(i, formula.Length - i));
                        numStack.Push(formula.Substring(i, index));
                        i = (i + index);
                    }//為操作數,獲取完整
                    else
                    {
                        if (formula[i] == '(')
                        {
                            opStack.Push(formula[i].ToString());
                        }
                        else if (formula[i] == ')')
                        {
                            MoveOperator(opStack, numStack);
                        }
                        else
                        {
                            if (opStack.Peek() == "(")
                            {
                                opStack.Push(formula[i].ToString());
                            }
                            else
                            {
                                JudgeOperator(opStack, numStack, formula[i].ToString());
                            }
                        }
                        i++;
                    }
                }
                if (opStack.Count != 0)
                {
                    while (opStack.Count != 0 && opStack.Peek() != "#")
                    {
                        numStack.Push(opStack.Pop());
                    }
                }

                StringBuilder strBuild = new StringBuilder();
                foreach (string s in numStack)
                {
                    strBuild.Insert(0,s);
                }
                Console.WriteLine(strBuild.ToString());
            }
        }

獲取運算符等級:

        /// <summary>
        /// 獲取運算符等級
        /// </summary>
        /// <param name="c">當前字符</param>
        /// <returns></returns>
        private static int GetOperationLevel(string c)
        {
            switch (c)
            {
                case "+": return 1;
                case "-": return 1;
                case "*": return 2;
                case "/": return 2;
                case "#": return -1;
                case "(": return -1;
                case ")": return -1;
                default: return 0;
            }
        }

獲取完整操作數

        /// <summary>
        /// 獲取完整數值
        /// </summary>
        /// <param name="formula">公式</param>
        /// <returns></returns>
        private static int GetCompleteValue(string formula)
        {
            int index = formula.Length;
            for (int i = 0; i < formula.Length; i++)
            {
                int num = GetOperationLevel(formula[i].ToString());
                if (num != 0)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

移動運算符:

  /// <summary>
        /// 移動運算符
        /// </summary>
        /// <param name="opStack"></param>
        /// <param name="numStack"></param>
        private static void MoveOperator(Stack<string> opStack, Stack<string> numStack)
        {
            string s = opStack.Pop();
            if (s == "(")
            {
                return;
            }
            else
            {
                numStack.Push(s);
                MoveOperator(opStack, numStack);
                return;
            }
        }

判斷運算符:

/// <summary>
        /// 判斷運算符
        /// </summary>
        /// <param name="opStack"></param>
        /// <param name="numStack"></param>
        /// <param name="x"></param>
        private static void JudgeOperator(Stack<string> opStack, Stack<string> numStack, string x)
        {
            int xNum = GetOperationLevel(x);
            int opNum = GetOperationLevel(opStack.Peek());
            if (xNum > opNum || numStack.Peek() == "(")
            {
                opStack.Push(x);
                return;
            }
            else
            {
                string opStr = opStack.Pop();
                numStack.Push(opStr);
                JudgeOperator(opStack, numStack, x);
                return;
            }
        }

代碼運行效果:

接下來就是計算公式的值,思路是新建一個棧R用來放結果,依次取出表達式的棧F,

1.如果取出的值為操作數,則放入棧R,

2.如果取出的值為運算符,則取出棧R頂的兩個,進行運算,運算結果在存放入棧R

最后R頂的元素,即為計算的值

下面為代碼,目前只做了+,-,*,/

       //numStack為逆波蘭表達式
                Stack<string> rpnFormula = new Stack<string>();
                foreach (string s in numStack)
                {
                    rpnFormula.Push(s);
                }
                Console.WriteLine(CalcRPNFormula(rpnFormula));=
   private static string CalcRPNFormula(Stack<string> rpnFormula)
        {
            Stack<string> resultStack = new Stack<string>();
            foreach (string s in rpnFormula)
            {
                int num = GetOperationLevel(s);
                if (num == 0)
                {
                    resultStack.Push(s);
                }
                else
                {
                    CalcResult(resultStack,s);
                }
            }
            return resultStack.Pop();
        }

        private static void CalcResult(Stack<string> resultStack, string operatorStr)
        {
            if (resultStack.Count >= 2)
            {
                double num2 = Convert.ToDouble(resultStack.Pop());
                double num1 = Convert.ToDouble(resultStack.Pop());
                if (operatorStr == "+")
                {
                    resultStack.Push(Convert.ToString(num1 + num2));
                }
                else if (operatorStr == "-")
                {
                    resultStack.Push(Convert.ToString(num1 - num2));
                }
                else if (operatorStr == "*")
                {
                    resultStack.Push(Convert.ToString(num1 * num2));
                }
                else if (operatorStr == "/")
                {
                    resultStack.Push(Convert.ToString(num1 / num2));
                }
            }         
        }

 因為適應自己的項目,需要做到支持“”^”,"arctan", "sqrt", "sin", "cos", "tan", "arcsin", "arccos"的計算方式,在查看上面生成的逆波蘭式的時候,發現^會后置,其他的都是前置,然后就投機取巧了以下,遇到^取前兩位,遇到其他特殊符號,則取后一位,所以增加了以下內容,測試了下例子:2^3+sqrt(9)+sin(45)+1 通過之前的代碼轉換為后綴表達式為23^sqrt9+sin45+1+(正確應為23^9sqrt+45sin+1+),然后根據上述的進行計算,加了這幾個特許計算后,可能與原來的后綴表達式有點背道而馳,沒有對特殊字符進行后置處理,但目前為了先實現計算,所以投機取巧了下,后期會進行修改

/// <summary>
        /// 特殊字符
        /// </summary>
        private static List<string> m_SpecialOp = new List<string>() { "arctan", "sqrt", "sin", "cos", "tan", "arcsin", "arccos" };

 /// <summary>
        /// 獲取運算符等級
        /// </summary>
        /// <param name="c">當前字符</param>
        /// <returns></returns>
        private static int GetOperationLevel(string c)
        {//sin(cos(tan(arcsin(arccos(arctan(
            switch (c)
            {
                case "+": return 1;
                case "-": return 1;
                case "*": return 2;
                case "/": return 2;
                case "^": return 2;
                case "arctan": return 10;
                case "sqrt": return 10;
                case "sin": return 10;
                case "cos": return 10;
                case "tan": return 10;
                case "arcsin": return 10;
                case "arccos": return 10;
                case "#": return -1;
                case "(": return -1;
                case ")": return -1;
                default: return 0;
            }
        }

/// <summary>
        /// 計算逆波蘭式
        /// </summary>
        /// <param name="rpnFormula"></param>
        /// <returns></returns>
        private static string CalcRPNFormula(Stack<string> rpnFormula)
        {
            Stack<string> resultStack = new Stack<string>();
            while (rpnFormula.Count > 0)
            {
                string rpnStr = rpnFormula.Pop();
                int num = GetOperationLevel(rpnStr);
                if (num == 0)
                {
                    resultStack.Push(rpnStr);
                }
                else if (num == 10)
                {
                    SpecialCalc(resultStack, rpnStr, rpnFormula.Pop());
                }
                else
                {
                    CalcResult(resultStack, rpnStr);
                }
            }
            return resultStack.Pop();
        }

        /// <summary>
        /// 特殊計算
        /// </summary>
        /// <param name="resultStack"></param>
        /// <param name="operatorStr"></param>
        /// <param name="calcValue"></param>
        private static void SpecialCalc(Stack<string> resultStack, string operatorStr, string calcValue)
        {
            if (m_SpecialOp.Contains(operatorStr))
            {
                double num = Convert.ToDouble(calcValue);
                if (operatorStr == "sqrt")
                {
                    resultStack.Push(Math.Sqrt(num).ToString("#0.000"));
                }
                else if (operatorStr == "sin")
                {
                    resultStack.Push(Math.Sin(num).ToString("#0.000"));
                }
                else if (operatorStr == "cos")
                {
                    resultStack.Push(Math.Cos(num).ToString("#0.000"));
                }
                else if (operatorStr == "tan")
                {
                    resultStack.Push(Math.Tan(num).ToString("#0.000"));
                }
                else if (operatorStr == "arcsin")
                {
                    resultStack.Push(Math.Asin(num).ToString("#0.000"));
                }
                else if (operatorStr == "arccos")
                {
                    resultStack.Push(Math.Acos(num).ToString("#0.000"));
                }
                else if (operatorStr == "arctan")
                {
                    resultStack.Push(Math.Atan(num).ToString("#0.000"));
                }
            }
        }

        /// <summary>
        /// 計算結果
        /// </summary>
        /// <param name="resultStack"></param>
        /// <param name="operatorStr"></param>
        private static void CalcResult(Stack<string> resultStack, string operatorStr)
        {
            if (resultStack.Count >= 2)
            {
                double num2 = Convert.ToDouble(resultStack.Pop());
                double num1 = Convert.ToDouble(resultStack.Pop());
                if (operatorStr == "+")
                {
                    resultStack.Push(Convert.ToString(num1 + num2));
                }
                else if (operatorStr == "-")
                {
                    resultStack.Push(Convert.ToString(num1 - num2));
                }
                else if (operatorStr == "*")
                {
                    resultStack.Push(Convert.ToString(num1 * num2));
                }
                else if (operatorStr == "/")
                {
                    resultStack.Push(Convert.ToString(num1 / num2));
                }
                else if (operatorStr == "^")
                {
                    resultStack.Push(Math.Pow(num1, num2).ToString());
                }
            }
        }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM