給出一個運算式 2*(3-1),迅雷不及掩耳,立馬知道答案為4,但是電腦可沒有這樣的能耐,它只知道直接計算,卻不知道優先順序。如此,我們需要自己用代碼來告訴它算符的優先順序
- 從左至右
- 先乘除後加減
- 先括弧內後括弧外
先來研究簡單的算術運算式,只有+-*/()運算子
算符優先表如,其中#為結束標識符。
現在來糾結具體的實現。
/// <summary>/// 返回兩運算子的優先順序/// </summary>/// <param name="first"></param>/// <param name="last"></param>/// <returns>">, < , ="</returns>private char Precede(char first, char last){ string operate = "+-*/()#"; char[,] level = new char[7, 7]{ {'>','>','<','<','<','>','>'}, {'>','>','<','<','<','>','>'}, {'>','>','>','>','<','>','>'}, {'>','>','<','<','<','>','>'}, {'<','<','<','<','<','=',' '}, {'>','>','>','>',' ','>','='}, {'<','<','<','<','<',' ','='} }; int rows = operate.IndexOf(first); int cols = operate.IndexOf(last); return level[rows, cols];}
主要是來展示表中的內容,返回>,<,=.
算術運算式的計算演算法,參數假設為標準的中綴運算式,否則會出現異常
/// <summary>/// 算符優先演算法/// </summary>/// <param name="infixExpression">中綴運算式</param>/// <returns></returns>public int ComputeExpression(string infixExpression){ Stack<int> stackOperand = new Stack<int>(); //運算元棧 Stack<char> stackOperator = new Stack<char>(); //操作符棧 stackOperator.Push('#'); //作為棧空的結束符 infixExpression = infixExpression + "#"; //中綴運算式的結束符 int temp = 0; int result = 0; int count = 0; char cur = infixExpression[count]; while (cur != '#' || stackOperator.Peek() != '#') //掃描完算術運算式,並且操作符棧為空白 { if (cur == ' ') continue; if (IsOperand(cur)) //運算元直接入棧 { stackOperand.Push(Int32.Parse(cur.ToString())); cur = infixExpression[++count]; //掃描算術運算式下一位 } else { switch (Precede(stackOperator.Peek(), cur)) //比較操作符棧頂元素和掃描的當前算符的優先順序 { //當前運算子優先順序較大,則直接入棧,置於棧頂(優先順序高需先計算) case '<': stackOperator.Push(cur); cur = infixExpression[++count]; break; //等於則表示棧頂元素為左括弧,當前字元為右括弧 case '=': stackOperator.Pop();//彈出左括弧 cur = infixExpression[++count]; break; //當前運算子優先順序小,則彈出棧頂運算子並從運算元棧彈出兩個運算元 case '>': temp = stackOperand.Pop(); result = stackOperand.Pop(); //注意計算的順序,計算結果入運算元棧,並且繼續比較新的棧頂操作符和當前操作符的優先順序 stackOperand.Push(Operate(result, temp, stackOperator.Pop())); break; } } } return stackOperand.Peek();}
以上方法是直接接受中綴運算式來計算結果,在應用方面有
計算機(更多的操作符,而且不一定都是二目運算子),相信也不難擴充
過濾方法(一般論壇的過濾演算法都是framework中的contains方法),contains方法只能為(s1and s2 and s3…),算符優先演算法則可以 ( s1and s2) or (s3) ) and s4等一系列的負責運算式
算符優先演算法還有一種是 先將標準的中綴運算式轉換為尾碼運算式(逆波蘭式),然後用一個用來儲存計算結果的棧來實現逆波蘭式計算。
不詳講
public string InfixToPostfix(string infixExpression){ Stack<char> stackOperand = new Stack<char>(); //運算元 Stack<char> stackOperator = new Stack<char>(); //運算子 for (int i = 0; i < infixExpression.Length; i++) { if (infixExpression[i] == ' ') continue; char oper = infixExpression[i]; switch (oper) { case '+': case '-': while (stackOperator.Count > 0) { char ch = stackOperator.Peek(); if (GetOperatorPriority('-') <= GetOperatorPriority(ch)) { stackOperator.Pop(); stackOperand.Push(ch); } else { break; } } stackOperator.Push(oper); break; case '*': case '/': while (stackOperator.Count > 0) { char ch = stackOperator.Peek(); if (GetOperatorPriority('*') <= GetOperatorPriority(ch)) { stackOperator.Pop(); stackOperand.Push(ch); } else { break; } } stackOperator.Push(oper); break; case '(': stackOperator.Push(oper); break; case ')': while (stackOperator.Count > 0 && stackOperator.Peek() != '(') { char ch = stackOperator.Pop(); stackOperand.Push(ch); } stackOperator.Pop(); break; default: stackOperand.Push(oper); break; } } while (stackOperator.Count > 0) { stackOperand.Push(stackOperator.Pop()); } StringBuilder sb = new StringBuilder(); for (int i = stackOperand.Count; i > 0; i--) { sb.Insert(0, stackOperand.Pop()); } return sb.ToString();}
public int ComputeArithmeticExpression(string postfixExpression) { Stack<int> stackResult = new Stack<int>(); int result = 0; int temp = 0; // for (int i = 0; i < postfixExpression.Length; i++) { char expr = postfixExpression[i]; switch (expr) { case '+': result = stackResult.Pop() + stackResult.Pop(); stackResult.Push(result); break; case '-': temp = stackResult.Pop(); result = stackResult.Pop() - temp; stackResult.Push(result); break; case '*': result = stackResult.Pop() * stackResult.Pop(); stackResult.Push(result); break; case '/': temp = stackResult.Pop(); result = stackResult.Pop() / temp; break; default: stackResult.Push(Int32.Parse(expr.ToString())); break; } } result = stackResult.Pop(); return result;}