这里如果对于形如字符串“((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++编程框架中,这两个概念并不 ...
-
[转]JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )
这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题: 先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(h ...
-
堆heap和栈Stack(百科)
堆heap和栈Stack 在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.在单片机应用中,堆栈 ...
-
Java中堆(heap)和栈(stack)的区别
简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存. 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配. 当在一段代码块定义一个变量时,Java就在栈中为这个变量分 ...
-
JVM 内存初学 堆(heap)、栈(stack)和方法区(method)
这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题:先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(he ...
随机推荐
-
LINQ系列:Linq to Object联接操作符
联接是指将一个数据源对象与另一个数据源对象进行关联或联合的操作.这两个数据源对象通过一个共同的值或属性进行关联. LINQ的联接操作符将包含可匹配(或相同)关键字的两个或多个数据源中的值进行匹配. L ...
-
Github注册流程和使用体验
大家好,我叫施蓓蓓,学号1413042063,在网络工程143班,我的兴趣爱好有很多,特别是在专业方面,比如软件工程.操作系统.网络通信技术.计算机组成原理等,我对游戏十分感兴趣,以后就业会朝这方面发 ...
-
HDU 4004
http://acm.hdu.edu.cn/showproblem.php?pid=4004 题意:青蛙过长L的河,只能落在石头上,石头数量是n,给出n个坐标,至多跳m次,求在可以过河的条件下,青蛙跳 ...
-
黄聪:wordpress后台导致fonts.googleapis.com、ajax.googleapis.com加载慢的解决方法
方法1.使用我做的插件.[googleapis-to-useso] 方法2.在functions.php文件里面添加下面的代码就行了. if(is_admin()) { function hcsem_ ...
-
hdu5017 Ellipsoid(旋转)
比赛的时候跳进这个大坑里,最后代码是写出来了.看到好像很多都是模拟退火做的,下面提供一个奇怪的思路吧. ax^2+by^2+cz^2+dyz+exz+fxy=1(*) 通过一些奇特的YY我们可以知道这 ...
-
1002 A + B Problem II [ACM刷题]
这一段时间一直都在刷OJ,这里建一个博客合集,用以记录和分享算法学习的进程. github传送门:https://github.com/haoyuanliu/Online_Judge/tree/mas ...
-
vim与外部文件的粘帖复制
vim与外部文件的粘帖复制 ubuntu默认vim是不支持从外部文件与vim之间的粘帖复制,vim有自己的剪切版,分别是”0-”9,”-,”8,”+,”:,”/,”%,”i,这些都是vim的寄存器,可 ...
-
Linux防止ARP攻击的一些方法
方法一,最常用的绑定网关 一般服务器的网关是不会变动的,且vps也适用. 一.查看当前网关 [root@local@xiaohuai ~]# arp -a ? (218.65.22.122) at 8 ...
-
cache缓存的BUG
坑: 1.在使用这个模版代码开发的时候,当我们改变了数据库表的设置的时候,我们都要把本地的cache缓存文件删除一下. 如果不删除的话,当我们改变数据库设置的之后,程序读取数据是从本地的缓存文件里面读 ...
-
如何搭建apache服务?
为了日后便于查询,本文所涉及到的所有命令集合如下: chkconfig iptables off #关闭防火墙命令 在Centos7中使用的是chkconfig firewalld off vi /e ...