C#使用递归和堆栈计算含有小括号,加减乘除的表达式(2)

83 篇文章 3 订阅

接上一篇,上一篇我们使用递归消元的方式进行表达式计算

C#使用递归和堆栈计算含有小括号,加减乘除的表达式(1)_斯内科的博客-CSDN博客

这次我们使用栈Stack<T>的方式:

思路:

1.生成数字和运算符列表:根据运算符的优先级,依次添加到堆栈中,生成的列表不存在左右小括号,只有数字 以及 加减乘除运算符。

2.解析列表:计算数字与运算符列表,如果遇到数字时,就进入栈中。如果遇到任何一个运算符时,就使用该运算符计算前两个数字【连续出栈两个数字】,然后将结果放入栈中,直到栈没有任何运算符就终止。此时栈只有一个数字,就是最终结果

整体代码:

新建类文件StackCalculateUtil.cs,

关键类StackCalculateUtil源代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CalculateUseStackAndRecursionDemo
{
    class StackCalculateUtil
    {
        /// <summary>
        /// 显示计算过程事件
        /// </summary>
        public static event Action<string> EventDisplayProcess;
        /// <summary>
        /// 根据运算符的优先级,依次添加到堆栈中
        /// 最终返回的列表不存在左右小括号,只有数字 以及 加减乘除运算符
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static List<string> ArithmeticToList(string expression)
        {
            string outstr = string.Empty;
            //表达式 1+2+7*(12.3-(12+9)/3)-(1.1+6.6)+4*5.2
            Stack<string> stack = new Stack<string>();
            List<string> list = new List<string>();
            int i = 0;
            while (i < expression.Length)
            {
                if (expression[i] == '+' || expression[i] == '-')
                {
                    //遇到加减时,如果顶部元素不为 左小括号,就一直出栈,并放入到列表中
                    while (stack.Count > 0 && stack.Peek() != "(")
                    {
                        outstr = stack.Pop();
                        list.Add(outstr);
                    }
                    stack.Push(expression[i].ToString());
                    i++;
                }
                else if (expression[i] == '*' || expression[i] == '/')
                {
                    //遇到乘除时,如果顶部元素是 乘或除,就一直出栈,并放入到列表中
                    while (stack.Count > 0 && (stack.Peek() == "*" || stack.Peek() == "/"))
                    {
                        outstr = stack.Pop();
                        list.Add(outstr);
                    }
                    stack.Push(expression[i].ToString());
                    i++;
                }
                else if (expression[i] == '(')
                {
                    //遇到左括号时,直接入栈
                    stack.Push(expression[i].ToString());
                    i++;
                }
                else if (expression[i] == ')')
                {
                    //遇到右括号时,直接出栈
                    if (stack.Count == 0)
                    {
                        throw new Exception($"括号不是成对出现的,表达式【{expression}】非法");
                    }
                    outstr = stack.Pop();
                    //遇到右括号,就一直出栈,直到遇到左括号 就终止
                    while (stack.Count > 0 && (string.IsNullOrEmpty(outstr) || outstr != "("))
                    {
                        list.Add(outstr);
                        outstr = stack.Pop();
                    }
                    i++;
                }
                else
                {
                    //数字和小数点 一直累加,直到(字符串结束 或者 遇到运算符)
                    outstr = string.Empty;
                    while (i < expression.Length && expression[i] != '+' && expression[i] != '-'
                        && expression[i] != '*' && expression[i] != '/' && expression[i] != '(' && expression[i] != ')')
                    {
                        outstr += expression[i];
                        i++;
                    }
                    list.Add(outstr);
                }
            }
            //将剩余的字符出栈
            while (stack.Count > 0)
            {
                list.Add(stack.Pop());
            }
            return list;
        }

        /// <summary>
        /// 计算数字与运算符列表,如果遇到数字时,就进入栈中。如果遇到任何一个运算符时,就使用该运算符计算前两个数字【连续出栈两个数字】,
        /// 然后将结果放入栈中,直到栈没有任何运算符就终止。此时栈只有一个最终结果
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static string CalculateSequenceList(List<string> list)
        {
            Stack<string> stack = new Stack<string>();
            for (int i = 0; i < list.Count; i++)
            {
                double x;
                double y;
                double simpleResult;
                if (list[i] == "+")
                {
                    simpleResult = ValidateData(stack, OperatorSymbol.Addition, out x, out y);
                    //将计算结果放入栈中
                    stack.Push(simpleResult.ToString());
                    EventDisplayProcess?.Invoke($"已计算出【{x}+{y}={simpleResult}】,并将结果入栈");
                }
                else if (list[i] == "-")
                {
                    simpleResult = ValidateData(stack, OperatorSymbol.Subtract, out x, out y);
                    stack.Push(simpleResult.ToString());
                    EventDisplayProcess?.Invoke($"已计算出【{x}-{y}={simpleResult}】,并将结果入栈");
                }
                else if (list[i] == "*")
                {
                    simpleResult = ValidateData(stack, OperatorSymbol.Multiply, out x, out y);
                    stack.Push(simpleResult.ToString());
                    EventDisplayProcess?.Invoke($"已计算出【{x}*{y}={simpleResult}】,并将结果入栈");
                }
                else if (list[i] == "/")
                {
                    simpleResult = ValidateData(stack, OperatorSymbol.Divide, out x, out y);
                    stack.Push(simpleResult.ToString());
                    EventDisplayProcess?.Invoke($"已计算出【{x}/{y}={simpleResult}】,并将结果入栈");
                }
                else
                {
                    stack.Push(list[i]);
                }
            }
            if (stack.Count > 0)
            {
                return stack.Pop();
            }
            return string.Empty;
        }

        /// <summary>
        /// 验证序列是否含有两个元素,以及这两个元素是否可以转化为浮点数
        /// </summary>
        /// <param name="stack"></param>
        /// <param name="operatorSymbol"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static double ValidateData(Stack<string> stack, OperatorSymbol operatorSymbol, out double x, out double y)
        {
            if (stack.Count < 2)
            {
                throw new Exception($"非法的栈,集合个数少于2.栈集合为【{string.Join(",", stack)}】");
            }
            string yStr = stack.Pop();
            if (!double.TryParse(yStr, out y))
            {
                throw new Exception($"表达式【{yStr}】无法转化为浮点数");
            }
            string xStr = stack.Pop();
            if (!double.TryParse(xStr, out x))
            {
                throw new Exception($"表达式【{xStr}】无法转化为浮点数");
            }
            switch (operatorSymbol)
            {
                case OperatorSymbol.Multiply:
                    return x * y;
                case OperatorSymbol.Divide:
                    return x / y;
                case OperatorSymbol.Addition:
                    return x + y;
                case OperatorSymbol.Subtract:
                    return x - y;
            }
            return 0;
        }
    }
}

窗体FormCalculate的主要代码如下(忽略设计器自动生成的代码):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CalculateUseStackAndRecursionDemo
{
    public partial class FormCalculate : Form
    {
        public FormCalculate()
        {
            InitializeComponent();

            RecursionCalculateUtil.EventDisplayProcess += RecursionCalculateUtil_EventDisplayProcess;
            StackCalculateUtil.EventDisplayProcess += RecursionCalculateUtil_EventDisplayProcess;
        }

        private void RecursionCalculateUtil_EventDisplayProcess(string message)
        {
            this.BeginInvoke(new Action(() => 
            {
                if (rtxbDisplay.TextLength > 40960)
                {
                    rtxbDisplay.Clear();
                }
                rtxbDisplay.AppendText($"{DateTime.Now.ToString("HH:mm:ss.fff")}--->{message}\n");
                rtxbDisplay.ScrollToCaret();
            }));
        }

        private void btnRecursion_Click(object sender, EventArgs e)
        {
            txbResult.Clear();
            rtxbDisplay.Clear();
            try
            {
                string result = RecursionCalculateUtil.GetExpression(rtxbExpression.Text);
                txbResult.Text = result;
            }
            catch (Exception ex)
            {
                RecursionCalculateUtil_EventDisplayProcess($"出错:{ex.Message}");
                MessageBox.Show(ex.Message, "出错");
            }
        }

        private void btnStack_Click(object sender, EventArgs e)
        {
            txbResult.Clear();
            rtxbDisplay.Clear();
            try
            {
                List<string> list = StackCalculateUtil.ArithmeticToList(rtxbExpression.Text);
                RecursionCalculateUtil_EventDisplayProcess("获取到计算数字与运算符列表:\n" + string.Join("\n", list));
                RecursionCalculateUtil_EventDisplayProcess("【无小括号】开始数字和运算符计算:");
                string result = StackCalculateUtil.CalculateSequenceList(list);
                txbResult.Text = result;
            }
            catch (Exception ex)
            {
                RecursionCalculateUtil_EventDisplayProcess($"出错:{ex.Message}");
                MessageBox.Show(ex.Message, "出错");
            }
        }
    }
}

程序运行如图:

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值