这里如果对于形如字符串“((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#的更多相关文章

  1. java 解析四则混合运算表达式并计算结果

    package ch8; import java.util.LinkedList; import java.util.List; import java.util.Stack; /** * 四则混合运 ...

  2. (转)Java里的堆(heap)栈(stack)和方法区(method)(精华帖,多读读)

    [color=red][/color]<一> 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创建,既在栈 ...

  3. 关于使用栈将一般运算式翻译为后缀表达式并实现三级运算的方法及实例(cpp版)

    #include <iostream> #include <stack> #include <vector> #include <string> #de ...

  4. Java里的堆(heap)栈(stack)和方法区(method)

    基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法 ...

  5. (转)堆heap和栈stack

    一 英文名称 堆和栈是C/C++编程中经常遇到的两个基本概念.先看一下它们的英文表示: 堆――heap 栈――stack 二 从数据结构和系统两个层次理解 在具体的C/C++编程框架中,这两个概念并不 ...

  6. 栈(stack)、递归(八皇后问题)、排序算法分类,时间和空间复杂度简介

    一.栈的介绍: 1)栈的英文为(stack)2)栈是一个先入后出(FILO-First In Last Out)的有序列表.3)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的 ...

  7. [二进制漏洞]栈(Stack)溢出漏洞 Linux篇

    目录 [二进制漏洞]栈(Stack)溢出漏洞 Linux篇 前言 堆栈 堆栈(Stack)概念 堆栈数据存储方式 函数调用 函数调用C语言代码 函数调用过程GDB调试 函数Call返回原理 函数栈帧 ...

  8. [转]JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )

    这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题: 先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(h ...

  9. 堆heap和栈Stack(百科)

    堆heap和栈Stack 在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.在单片机应用中,堆栈 ...

随机推荐

  1. C#集合--数组

    Array类是所有一维和多维数组的隐式基类,同时也是实现标准集合接口的最基本的类型.Array类实现了类型统一,因此它为所有数组提供了一组通用的方法,不论这些数组元素的类型,这些通用的方法均适用. 正 ...

  2. OllyICE 调试的程序无法处理异常 解决方法

    问题描述 在用OllyICE打开可执行文件时出现如下图所示错误 解决方法 1. 选项 -> 调试设置 , 打开调试选项 2. 切换到 异常 页签 3. 取消勾选 忽略(传递给程序)以下异常: 单 ...

  3. Android之输入框光标和Hint的位置

    如图所示,要实现这一的需求,一般人的布局方式就是左边一button,右边一button,中间一个EditText,为了输入框的响应触摸范围更大往往不会把宽度设置为wrap_content,要么设置成m ...

  4. 逆转序列的递归/尾递归(+destructuring assignment)实现(JavaScript + ES6)

    这里是用 JavaScript 做的逆转序列(数组/字符串)的递归/尾递归实现.另外还尝鲜用了一下 ES6 的destructuring assignment + spread operator 做了 ...

  5. easyui+Spring MVC+hibernate = 乐途

    这个东西,玩的差不多了;不浪费口水了, 直接上图 发到blog 上让大家看看. 布局各方面有没有不足的地方 .请多多指教 http://item.taobao.com/item.htm?spm=686 ...

  6. MyBatis知多少(10)应用程序数据库

    应用程序数据库往往是最小.最简单.也最易于使用的数据库.这种数据库往往是我们这些开发人员通常不介意使用甚至非常乐意使用的.应用程序数据库通常与我们的应用程序处于同一个项目中,两者一齐设计和实现.正是因 ...

  7. JS基础回顾,小练习(克隆对象,数组)

    对象的克隆: var srcObj = { a: 1, b: { b1: ["hello", "hi"], b2: "JavaScript" ...

  8. AssetBundle系列——资源的加载、简易的资源管理器

    每个需要进行资源管理的类都继承自IAssetManager,该类维护它所使用到的所有资源的一个资源列表.并且每个资源管理类可以重写其资源引用接口和解引用接口. 每个管理器有自己的管理策略,比如Scen ...

  9. AutoMapper用法一瞥

    前段时候做个小项目用到了autoMapper(参考了NOP里的用法),感觉还行,用起来挺方便的.首先复杂的东西我就不说了,至于详细的教程我也就不写了,比较园子里有很多这样的文章,我就分享下,在项目中实 ...

  10. python函数式编程

    函数式编程是使用一系列函数去解决问题,按照一般编程思维,面对问题时我们的思考方式是“怎么干”,而函数函数式编程的思考方式是我要“干什么”. 至于函数式编程的特点暂不总结,我们直接拿例子来体会什么是函数 ...