接上一篇,上一篇我们使用递归消元的方式进行表达式计算
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, "出错");
}
}
}
}
程序运行如图: