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