Stack<T>和Stack则是后进先出的数据结构,通过Push和Pop法实现添加元素到队列的顶部和从队列的顶部移除元素。同样也提供了Peek方法、Count属性和ToArray方法。
栈的实践使用:逆波兰公式
对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:
using System; using System.Collections.Generic; using System.Diagnostics; //System.Diagnostics命名空间 包含了能够与系统进程 事件日志 和性能计数器进行交互的类 //一般用于帮助诊断和调试应用程序 //查考链接 https://www.douban.com/note/220654090/ namespace ConsoleAppTest { class Program { //是否为数字 //判断是否为整数字符串 static bool isNumber(string message) { //判断是否为整数字符串 //是的话则将其转换为数字并将其设为out类型的输出值、返回true, 否则为false int result = -1; //result 定义为out 用来输出值 try { //当数字字符串的为是少于4时,以下三种都可以转换,任选一种 //如果位数超过4的话,请选用Convert.ToInt32() 和int.Parse() //result = int.Parse(message); //result = Convert.ToInt16(message); result = Convert.ToInt32(message); return true; } catch { return false; } } //是否为操作符 static bool isOpertor(string c) { return c == "+" || c == "-" || c == "*" || c == "/" || c == "(" || c == ")"; } //判断操作符优先级大小 static bool comparePriority(string op1, string op2) { return getPriorityValue(op1) > getPriorityValue(op2); } //获取操作符优先级 static int getPriorityValue(string c) { int priority; switch (c) { case "+": priority = 1; break; case "-": priority = 1; break; case "*": priority = 2; break; case "/": priority = 2; break; default: priority = 0; break; } return priority; } //计算值 static int getOperand(string op, int num1, int num2) { int result = -1; switch (op) { case "+": result = num1 + num2; break; case "-": result = num1 - num2; break; case "*": result = num1 * num2; break; case "/": result = num1 / num2; break; } return result; } static Stack<string> changeExpression(List<string> beforeExps) { Stack<string> operand = new Stack<string>();//操作数 Stack<string> opertor = new Stack<string>();//操作符 //遍历中序表示 int length = beforeExps.Count; //判断是否为操作数 for (int i = 0; i < length; i++) { string c = beforeExps[i]; if (isNumber(c)) { //操作数 存在操作数栈 operand.Push(c); } else { //为运算符 //若运算符为"("直接存入到运算符栈中 if (c == "(") { opertor.Push(c); } else if (c == ")") { //该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止。 将"("出栈 while (opertor.Peek() != "(") { string stringvalue = opertor.Pop(); operand.Push(stringvalue); } opertor.Pop(); } else { // 该运算符为非括号运算符: //考虑栈顶为空的情况 if (opertor.Count <= 0) { opertor.Push(c); continue; } // (a) 若运算符堆栈栈顶的运算符为括号,则直接存入运算符堆栈。 ////符合为左括号 直接存入运算符 if (opertor.Peek() == "(") { opertor.Push(c); } else { //(b) 若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈。 if (comparePriority(c, opertor.Peek())) { opertor.Push(c); } else { // (c) 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。 string stringvalue = opertor.Pop(); operand.Push(stringvalue); opertor.Push(c); } } } } } //4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。 while (opertor.Count > 0) { string stringvalue = opertor.Pop(); operand.Push(stringvalue); } //反转operand 获取正常的会缀表达式 Stack<string> resultSt = new Stack<string>(); while (operand.Count > 0) { string stringvalue = operand.Pop(); resultSt.Push(stringvalue); } return resultSt; } //转换string为list表 static List<string> changeStrToList(string str) { List<string> resultSt = new List<string>(); List<int> sortNum = new List<int>(); bool isConNum = false; foreach(char c in str) { if (isOpertor(c.ToString())) { if (isConNum && sortNum.Count > 0) { //添加数字 int num = 0; for (int i = sortNum.Count -1 ; i >= 0; i--) { if (i == sortNum.Count - 1) { num = num + sortNum[i]; }else{ num = num + sortNum[i] * 10 * (sortNum.Count - 1 - i ); } } resultSt.Add(num.ToString()); sortNum.Clear(); } isConNum = false; //如果是操作符直接添加 resultSt.Add(c.ToString()); }else{ //如果是数字 isConNum = true; sortNum.Add(int.Parse(c.ToString())); } } if (sortNum.Count > 0) { //添加数字 int num = 0; for (int i = sortNum.Count - 1; i >= 0; i--) { if (i == sortNum.Count - 1) { num = num + sortNum[i]; } else { num = num + sortNum[i] * 10 * (sortNum.Count - 1 - i); } } resultSt.Add(num.ToString()); sortNum.Clear(); } return resultSt; } //计算逆波兰公式 static int calculateExpression(Stack<string> st) { //临时存储计算数据 Stack<string> reslutSt = new Stack<string>(); while(st.Count > 0) { string numStr = st.Peek(); if (isNumber(numStr)) { //如果字符是一个操作数,把它压入堆栈。 reslutSt.Push(numStr); } else { //如果字符是个操作符,弹出两个操作数,执行恰当操作, //然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。 int number1 = int.Parse(reslutSt.Pop()); int number2 = int.Parse(reslutSt.Pop()); int value = getOperand(numStr, number2, number1); reslutSt.Push(value.ToString()); } st.Pop(); } return int.Parse(reslutSt.Peek()); } static void Main(string[] args) { Console.WriteLine("C# 逆波兰公式 输入您要计算的公式,按enter结束"); //string inputStr = Console.ReadLine(); string teststr = Console.ReadLine(); List<string> inputStr = changeStrToList(teststr); Console.WriteLine("公式为:"); foreach (string str in inputStr) { Console.Write("{0} ", str); } Console.Write(" ===> "); Stack<string> changeSt = changeExpression(inputStr); foreach (string str in changeSt) { Console.Write("{0} ", str); } Console.WriteLine("逆波兰公式计算:"); //计算公式的值 int resultValue = calculateExpression(changeSt); Console.WriteLine("{0} = {1} ", teststr ,resultValue); Console.ReadKey(); } } }