使用栈Stack对整数数值的运算表达式字符串进行运算C#
这里如果对于形如字符串“((6+((7+8)-9)*9+8/2)-3)/2”的运算表达式进行运算。接触过此类的同学知道这种存在着运算符优先级的表达式,不能直接从左到右进行运算,我们使用OperandStack操作数栈和OperatorStack操作符栈,对操作符进行比较,确定优先级后,取出操作数进行运算。
算法思想如下:
1.首先确定操作的符的优先级,*、/大于+、-,(大于*、/,*、/或者+、-一起时,排在前面的运算符优先,)的优先级最小,且)与(相遇时抵消
2.从左到右遍历字符串,每次遍历一个字符,设置数字临时存储变量OperandTemp
①当遇到操作符时, 如果OperandTemp有数值,把数字压入到OperandStack中
②循环OperatorStack,直到OperatorStack没值为止,然后比较这个操作符和OperatorStack顶部的运算符进行比较,如果此操作符运算优先级高,将此运算符压入栈,退出循环;如果此操作符运算优先级低,则将OperatorStack栈顶的运算符取出ope1,从OperandStack中取出顶部的两个数值a1(先出栈)和a2,注意首先出栈的做第二个操作数,则进行
a2 ope1 a1;求得结果压人OperandStack中;如果此操作符是)遇到(时,将OperatorStack栈中的(消除
③循环到最后会剩余2中情况,即OperatorStack中剩1个运算符,剩余两个运算符,且最后一个运算符优先级高,则读取最后一个数字和OperandStack顶的数字进行操作运算,求得结果,再运算。
以字符串“((6+((7+8)-9)*9+8/2)-3)/2”为例,算法的运算过程是,先将((压入OperatorStack变为【((】,将6压入OperandStack【6】,到+,由于(不参与运算,则将+压入
OperatorStack【((+】,下一个字符(的优先级大于OperatorStack顶的+,则将(压入OperatorStack【((+(】,下一个(压入OperatorStack【((+((】,7压入OperandStack【6,7】,下一个+同理压入
OperatorStack【((+((+】,8压入OperandStack【6,7,8】,下一个)优先级小于OperatorStack顶部的+,则取出OperandStack顶部的8和7和OperatorStack顶部的+,运算得15压入OperandStack,继续比较)和OperatorStack顶的(,优先级相同,同时消去(,此时OperatorStack为【((+(】,OperandStack位【6,15】,下一个字符-小于(,入栈【((+(—】,9入栈OperandStack为【6,15,9】;下一个),取出-和15,9进行运算得6,入栈OperandStack【6,6】;)消去栈顶(OperatorStack为【((+】;下一个*同理,OperandStack【6,6】,OperatorStack为【((+*】;下一个9入栈OperandStack【6,6,9】;下一个+,优先级小于*,则6*9=54入栈,OperandStack【6,54】,OperatorStack为【((+】;下一个+,优先级小,则取出栈顶+6+54=60入栈OperandStack【60】,压入此运算符+OperatorStack为【((+】;下一个8入栈OperandStack【60,8】;下一个/,优先级大入栈,2入栈,则OperatorStack为【((+/】,OperandStack【60,8,2】;下一个)优先级小,则
OperatorStack为【((+】,OperandStack【60,4】=》OperatorStack为【(】,OperandStack【64】;下一个-入栈,3入栈OperatorStack为【(-】,OperandStack【64,3】;下一个),则64-3=61入栈,OperatorStack为【空】,OperandStack【61】;下一个/入栈,2入栈,OperatorStack为【/】,OperandStack【61,2】;此为剩下一操作符的情况,最后运算得到结果:61/2=30.5
实现代码如下:
static char[] Operators = new char[] { '+', '-', '*', '/', '(', ')' };
static void Main(string[] args)
{
float a = EvaluateExpression("10+(80*3+(6+7))*2");
Console.WriteLine(a);
Console.ReadKey();
}
/// <summary>
/// 初始化运算符优先级
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
static char InitPriorities(char a, char b)
{
int aIndex = -;
int bIndex = -;
for (int i = ; i < Operators.Length; i++)
{
if (Operators[i] == a)
aIndex = i;
if (Operators[i] == b)
bIndex = i;
}
char[,] Priorities = new char[, ] {{'>','>','<','<','<','>'},
{'>','>','<','<','<','>'},
{'>','>','>','>','<','>'},
{'>','>','>','>','<','>'},
{'<','<','<','<','<','='},
{'?','?','?','?','?','?'}};
return Priorities[aIndex, bIndex];
}
static float Calculate(float Operand1, float Operand2, char Operator)
{
float Ret = ;
if (Operator == '+')
{
Ret = Operand1 + Operand2;
}
else if (Operator == '-')
{
Ret = Operand1 - Operand2;
}
else if (Operator == '*')
{
Ret = Operand1 * Operand2;
}
else if (Operator == '/')
{
Ret = Operand1 / Operand2;
}
return Ret;
}
static float EvaluateExpression(string str)
{
Stack<float> OperandStack = new Stack<float>(); // 操作数栈,
Stack<char> OperatorStack = new Stack<char>(); // 操作符栈
float OperandTemp = ;
char LastOperator = ''; // 记录最后遇到的操作符
for (int i = , size = str.Length; i < size; ++i)
{
char ch = str[i];
if ('' <= ch && ch <= '')
{ // 读取一个操作数
OperandTemp = OperandTemp * + ch - '';
}
else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
ch == '(' || ch == ')')
{
// 有2种情况 是没有操作数需要入栈保存的。
// 1 当前操作符是 “(”。(的左边的操作符已经负责操作数入栈了。
// 2 上一次遇到的操作符是“)”。)本身会负责操作数入栈,)后面紧跟的操作符不需要再负责操作数入栈。
if (ch != '(' && LastOperator != ')')
{
// 遇到一个操作符后,意味着之前读取的操作数已经结束。保存操作数。
OperandStack.Push(OperandTemp);
// 清空,为读取下一个操作符做准备。
OperandTemp = ;
}
// 当前遇到的操作符作为操作符2,将和之前遇到的操作符(作为操作符1)进行优先级比较
char Opt2 = ch;
for (; OperatorStack.Count > ; )
{
// 比较当前遇到的操作符和上一次遇到的操作符(顶部的操作符)的优先级
char Opt1 = OperatorStack.Peek();
char CompareRet = InitPriorities(Opt1, Opt2);
if (CompareRet == '>')
{ // 如果操作符1 大于 操作符2 那么,操作符1应该先计算
// 取出之前保存的操作数2
float Operand2 = OperandStack.Pop();
// 取出之前保存的操作数1
float Operand1 = OperandStack.Pop();
// 取出之前保存的操作符。当前计算这个操作符,计算完成后,消除该操作符,就没必要保存了。
OperatorStack.Pop();
// 二元操作符计算。并把计算结果保存。
float Ret = Calculate(Operand1, Operand2, Opt1);
OperandStack.Push(Ret);
}
else if (CompareRet == '<')
{ // 如果操作符1 小于 操作符2,说明 操作符1 和 操作符2 当前都不能进行计算,
// 退出循环,记录操作符。
break;
}
else if (CompareRet == '=')
{
// 操作符相等的情况,只有操作符2是“)”,操作数1是“(”的情况,
// 弹出原先保存的操作符“(”,意味着“(”,“)”已经互相消掉,括号内容已经计算完毕
OperatorStack.Pop();
break;
}
} // end for
// 保存当前遇到操作符,当前操作符还缺少右操作数,要读完右操作数才能计算。
if (Opt2 != ')')
{
OperatorStack.Push(Opt2);
}
LastOperator = Opt2;
}
} // end for
/*
上面的 for 会一面遍历表达式一面计算,如果可以计算的话。
当遍历完成后,并不代表整个表达式计算完成了。而会有2种情况:
1.剩余1个运算符。
2.剩余2个运算符,且运算符1 小于 运算符2。这种情况,在上面的遍历过程中是不能进行计算的,所以才会被遗留下来。
到这里,已经不需要进行优先级比较了。情况1和情况2,都是循环取出最后读入的操作符进行运算。
*/
if (LastOperator != ')')
{
OperandStack.Push(OperandTemp);
}
for (; OperatorStack.Count > ; )
{
// 取出之前保存的操作数2
float Operand2 = OperandStack.Pop();
// 取出之前保存的操作数1
float Operand1 = OperandStack.Pop();
// 取出末端一个操作符
char Opt = OperatorStack.Pop();
// 二元操作符计算。
float Ret = Calculate(Operand1, Operand2, Opt);
OperandStack.Push(Ret);
}
return OperandStack.Peek();
}
使用栈Stack对整数数值的运算表达式字符串进行运算C#的更多相关文章
- java 解析四则混合运算表达式并计算结果
package ch8; import java.util.LinkedList; import java.util.List; import java.util.Stack; /** * 四则混合运 ...
- (转)Java里的堆(heap)栈(stack)和方法区(method)(精华帖,多读读)
[color=red][/color]<一> 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收. 引用数据类型,需要用new来创建,既在栈 ...
- 关于使用栈将一般运算式翻译为后缀表达式并实现三级运算的方法及实例(cpp版)
#include <iostream> #include <stack> #include <vector> #include <string> #de ...
- Java里的堆(heap)栈(stack)和方法区(method)
基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收. 引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法 ...
- (转)堆heap和栈stack
一 英文名称 堆和栈是C/C++编程中经常遇到的两个基本概念.先看一下它们的英文表示: 堆――heap 栈――stack 二 从数据结构和系统两个层次理解 在具体的C/C++编程框架中,这两个概念并不 ...
- 栈(stack)、递归(八皇后问题)、排序算法分类,时间和空间复杂度简介
一.栈的介绍: 1)栈的英文为(stack)2)栈是一个先入后出(FILO-First In Last Out)的有序列表.3)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的 ...
- [二进制漏洞]栈(Stack)溢出漏洞 Linux篇
目录 [二进制漏洞]栈(Stack)溢出漏洞 Linux篇 前言 堆栈 堆栈(Stack)概念 堆栈数据存储方式 函数调用 函数调用C语言代码 函数调用过程GDB调试 函数Call返回原理 函数栈帧 ...
- [转]JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )
这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题: 先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(h ...
- 堆heap和栈Stack(百科)
堆heap和栈Stack 在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.在单片机应用中,堆栈 ...
随机推荐
- How to import library ?
Android Studio: Download or git the library. (for example: the library folder named ActionBarSherloc ...
- 在WPF中使用字体图标
一.源码描述 这是一款基于WPF窗体应用程序的字体图标示例源码, 该源码简单易懂使用于初学者和实战项目应用, 感兴趣的朋友们可以下载看看哦. 二.功能介绍 1.用ICO字体代替 ...
- Ecshop文章列表页显示内容摘要
本教程中讲到的“内容摘要”指的是文章内容的前 60个字符(当然也可以是前40个,前50个等等) 下面以 2.7.2版 + 官方默认模板 为例进行讲解: 1).修改 includes/lib_artic ...
- 【转】中国正爆发聊天APP大战 未来或影响西方
[搜狐IT消息]8月15日消息,<金融时报>报道称,在中国,聊天应用程序的竞争日渐激烈,腾讯.阿里巴巴都加入大战,在西方市场的未来竞争中,中国聊天应用可能会成为先驱. 一些分析师认为,快速 ...
- 实现 Dispose 方法
实现 Dispose 方法 MSDN 类型的 Dispose 方法应释放它拥有的所有资源.它还应该通过调用其父类型的 Dispose 方法释放其基类型拥有的所有资源.该父类型的 Dispose 方法应 ...
- SQL查询 - 表连接
一.连接查询简介 连接查询中用来连接连个表的条件称为连接条件或连接谓词.其形式为: [<表1>].<列名1><连接运算符>[<表2>].<列2&g ...
- MySql 数据操作类
/// <summary> /// MySqlHelper 的摘要说明. /// </summary> public class MySqlHelper { public st ...
- ODAC(V9.5.15) 学习笔记(十六)直接访问模式
直接访问模式(Direct mode)是ODAC最大的特色之一,即不需要安装Oracle客户端,ODAC越过了OCI(Oracle Call Interface ),使用TCP/IP协议就可以直接与O ...
- JRE 1.8.0_65/66 Certified with Oracle E-Business Suite
Java Runtime Environment 1.8.0_65 (a.k.a. JRE 8u65-b17) and JRE 1.8.0_66 (8u66-b17) and later update ...
- 30分钟入门Java8之默认方法和静态接口方法
30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...