美文网首页
【数据结构】【C#】011-栈的应用:📟表达式求值

【数据结构】【C#】011-栈的应用:📟表达式求值

作者: lijianfex | 来源:发表于2018-10-13 20:39 被阅读67次

    C#数据结构:栈的应用:表达式求值

    后缀表达式

    在我们日常生活中所见表达式都是中缀表达式,如 “5*(3+7)-4/2”,这中表达式符合我们的思维逻辑,可读性强,但是不利于计算机的解析。由波兰逻辑学家J.Lukasiewicz发明出后缀表达式,比如上式转变为后缀表达式”5 3 7 + * 4 2 / -“,这种人类难以适应的表达顺序,计算机却很受用。

    中缀表达式转后缀表达式

    如:中缀表达式”5*(3+7)-4/2”转为”5 3 7 + * 4 2 / -“

    规则:顺序遍历数字和符号,数字输出,成为后缀表达式的一部分,遇符号则判断栈顶元素与其的优先级,若为右括号或者优先级不高于栈顶元素,则将栈顶元素依次出栈并输出,并将当前符号进栈,直到后缀表达式输出完成。

    ①5输出,’’入栈,’(‘入栈,3输出,’+’入栈,7输出。
    输出:5 3 7
    ②遇到’)’,则将’(‘之前的符号全部出栈输出。
    输出:5 3 7 +
    ③遇到’-‘,优先级比栈顶’
    ‘低,’* ‘出栈输出,’-‘进栈。
    输出:5 3 7 + *
    ④输出4,遇到’/’比栈顶’-‘高,’/’进栈,输出2,表达式读取结束,栈内符号依次输出。
    输出:5 3 7 + * 4 2 / -
    中缀表达式转后缀表达式结束

    计算机应用后缀表达式的过程:

    如后缀表达式:”5 3 7 + * 4 2 / -”

    规则:从左到右遍历表达式的每一个数字和符号,遇到数字就进栈,遇到是符号,就将处于栈顶的两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。

    ①初始化一个空栈。此栈用来对要运算的数字进出使用。
    ②后缀表达式中前三个都是数字,所以5,3,7进栈。
    ③接下来是’+’运算符,将栈顶的两个元素出栈进行加法运算,再将结果进栈。
    ④之后是’*’运算符,将栈顶的两个元素出栈进行运算,将运算结果再进栈。
    ⑤之后4,2进栈,遇’/’将2,4出栈,2作为除数,4作为被除数。
    ⑥之后遇’-‘,50作为被减数。48入栈,最后出栈,栈为空结果为48.


    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    /// <summary>
    /// 栈的应用
    /// </summary>
    public class StackAppliction
    {
        #region 1、括号匹配问题 :({[]})
        /// <summary>
        /// 括号匹配算法(栈的应用)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool BracketMatch(string str)
        {
            if (str == string.Empty || str == null)//校验字符括号是否为空
            {
                Debug.Log("字符括号为空!括号匹配失败!");
                return false;
            }
    
            LinkStack<char> stack = new LinkStack<char>(); //用来存储左括号(此处使用了之前自己定义的链栈结构)
            //Stack<char> stack = new Stack<char>();//系统提供的栈
    
            for (int i = 0; i < str.Length; i++)//读取括号字符串
            {
                switch (str[i])
                {
                    case '(':
                    case '[':
                    case '{':
                        stack.Push(str[i]);//左括号进栈
                        break;
                    case ')':
                    case ']':
                    case '}':
                        if (stack.IsEmpty())//使用系统提供的栈:stack.Count==0
                        {
                            Debug.Log("右括号   " + str[i] + "  多余!括号匹配失败!");
                            return false;
                        }
                        else
                        {
                            char left = stack.Peek();//访问左括号栈顶值
    
                            if (Match(left, str[i])) //判断左右括号类型
                            {
                                stack.Pop(); //匹配成功,出栈顶值
                            }
                            else
                            {
                                Debug.Log("左括号:  " + left + "  与右括号:   " + str[i] + "   不同类型!括号匹配失败! ");
                                return false;
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
    
            if (stack.IsEmpty())//使用系统提供的栈:stack.Count==0
            {
                Debug.Log("括号匹配成功!");
                return true;
            }
    
            Debug.Log("左括号:  " + stack.Peek() + "   多余!括号匹配失败!");
            return false;
    
        }
    
        /// <summary>
        /// 判断左括号与右括号是否同类型
        /// </summary>
        /// <param name="l">左括号</param>
        /// <param name="r">右括号</param>
        /// <returns></returns>
        private bool Match(char l, char r)
        {
            if ((l == '(') && (r == ')'))
            {
                return true;
            }
            if ((l == '[') && (r == ']'))
            {
                return true;
            }
            if ((l == '{') && (r == '}'))
            {
                return true;
            }
            return false;
    
        }
    
        #endregion
    

    由于需要检查表达式的括号是否匹配,上方的代码块被引入;

    下方是具体的表达式求值过程:

        #region 2、表达式求值:  "5 * ( ( 3 + 7 )+(3*2)^2 ) - 4 / 2"
        //2、表达式求值:
        public int ExpEvaluation(string middleExp)
        {
    
    
            //1、将str 中缀表达式转为后缀表达式
            string TrailExp = ToTrailExp(middleExp);
    
    
            //2、对后缀表达式进行求值操作
            return TrailExpEvalution(TrailExp);
    
        }
    
        /// <summary>
        /// 中缀表达式转为后缀表达式
        /// </summary>
        /// <param name="str">中缀表达式</param>
        /// <returns>后缀表达式</returns>
        public string ToTrailExp(string str)
        {
            string traiExp = null;
    
            if (str == null || str == string.Empty)//校验str是否空
            {
                return null;
            }
    
    
            if (!BracketMatch(str))//判断是否平衡圆括号
            {
                Debug.Log("表达式的圆括号不平衡,表达式有误!");
                return null;
            }
    
            Stack<char> optrStack = new Stack<char>(); //操作符栈
    
            for (int i = 0; i < str.Length; i++)
            {
                switch (str[i])
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        traiExp += str[i];
                        break;
                    case ' ':
                        break;
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                    case '^':
                        if (optrStack.Count == 0)
                        {
                            optrStack.Push(str[i]);
                        }
                        else
                        {
                            char op = optrStack.Peek();
                            if (op == '(')
                            {
                                optrStack.Push(str[i]);
                            }
                            else
                            {
                                if (Priority(str[i]) <= Priority(op))
                                {
                                    traiExp += optrStack.Pop();
                                    optrStack.Push(str[i]);
                                }
                                else
                                {
                                    optrStack.Push(str[i]);
                                }
                            }
                        }
                        break;
                    case '('://碰到左括号,直接入栈
                        optrStack.Push(str[i]);
                        break;
                    case ')': //一直出栈至首次出栈的"("
                        while (optrStack.Count > 0 && optrStack.Peek() != '(')
                        {
                            traiExp += optrStack.Pop();
                        }
                        if (optrStack.Count != 0)
                        {
                            optrStack.Pop();
                        }
                        break;
                    default:
                        break;
                }
            }
    
            while (optrStack.Count != 0) //把剩余操作符出栈并输出
            {
                traiExp += optrStack.Pop();
            }
    
            return traiExp;
    
        }
    
        /// <summary>
        /// 判断操作符的优先级
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private int Priority(char c)
        {
            switch (c)
            {
                case '+':
                case '-':
                    return 1;
                case '*':
                case '/':
                    return 2;
                case '^':
                    return 3;
                default:
                    return -1;
            }
        }
    
    
        /// <summary>
        /// 对后缀表达式进行求值操作
        /// </summary>
        /// <param name="trailExp">后缀表达式</param>
        /// <returns>结果</returns>
        public int TrailExpEvalution(string trailExp)
        {
            if (trailExp == null || trailExp == string.Empty) //校验是否空
            {
                return -1;
            }
    
            Stack<int> numStack = new Stack<int>();
    
            for (int i = 0; i < trailExp.Length; i++)
            {
                int n1, n2;
    
                switch (trailExp[i])
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        numStack.Push(int.Parse(trailExp[i].ToString()));
                        break;
                    case ' ':
                        break;
                    case '+':
                        n1 = numStack.Pop();
                        n2 = numStack.Pop();
                        numStack.Push(n1 + n2);
                        break;
                    case '-':
                        n1 = numStack.Pop();//减数
                        n2 = numStack.Pop();//被减数
                        numStack.Push(n2 - n1);
                        break;
                    case '*':
                        n1 = numStack.Pop();
                        n2 = numStack.Pop();
                        numStack.Push(n2 * n1);
                        break;
                    case '/':
                        n1 = numStack.Pop();//除数
                        n2 = numStack.Pop();//被除数
                        numStack.Push(n2 / n1);
                        break;
                    case '^':
                        n1 = numStack.Pop();//幂数
                        n2 = numStack.Pop();//低数
                        numStack.Push((int)Math.Pow(n2, n1));
                        break;
                    default:
                        break;
                }
            }
            return numStack.Pop();
        }
    
        #endregion
    
    }
    

    表达式求值算法测试:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    
    public class _010_StackAppliction : MonoBehaviour
    {
    
        void Start()
        {
    
    
            StackAppliction appliction = new StackAppliction();
    
            string str = "5 * ( ( 3 + 7 )+(3*2)^2 ) - 4 / 2";
    
            print("中缀表达式: " + str);
    
            print("后缀表达式: " + appliction.ToTrailExp(str));
    
            print("表达式结果: " + appliction.ExpEvaluation(str));
    
    
    
        }
    }
    

    输出结果:


    img.jpg

    注:

    1、主要利用栈的先进后出特性,可以算是一个简单的计算器

    2、该例子只适用于运算数为0-9的个位数,两位数需要另写算法,将两个相邻数字处理为一个整体。(可以思考下处理方式!)

    相关文章

      网友评论

          本文标题:【数据结构】【C#】011-栈的应用:📟表达式求值

          本文链接:https://www.haomeiwen.com/subject/jqhaaftx.html