数据结构学习笔记三:算符优先算法

给出一个表达式 2*(3-1),迅雷不及掩耳,立马知道答案为4,但是计算机可没有这样的能耐,它只知道直接计算,却不知道优先级。如此,我们需要自己用代码来告诉它算符的优先级

  1. 从左至右
  2. 先乘除后加减
  3. 先括号内后括号外

先来研究简单的算术表达式,只有+-*/()运算符

untitled

算符优先表如上图,其中#为结束标识符。

现在来纠结具体的实现。

/// <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;

}

你可能感兴趣的:(数据结构)