给出一个表达式 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;
}
















