这里如果对于形如字符串“((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. android sudio 打包资料汇总

    .http://blog.csdn.net/fengyuzhengfan/article/details/43876489 混淆2.http://my.oschina.net/fallenpanda/ ...

  2. iOS AVKit音视频播放全面详解

    公司项目中经常要用到音视频处理,也需要去定制一些东西,然后整理这些音视频处理就显得尤为重要!方便自己和广大朋友学习收藏! 以下参考连接特别重要: 苹果官方:AVKit API 苹果官方:AVFound ...

  3. C# winForm 窗体闪烁问题

    在构造函数里加上以下代码: this.DoubleBuffered = true;//设置本窗体            SetStyle(ControlStyles.UserPaint, true); ...

  4. PowerShell实现文件下载(类wget)

    对Linux熟悉的读者可能会对Linux通过wget下载文件有印象,这个工具功能很强大,在.NET环境下提到下载文件大多数人熟悉的是通过System.Net.WebClient进行下载,这个程序集能实 ...

  5. Day One studying english

    I start study english lately,but the is no basis for english.Only i use baidu translation,google tra ...

  6. UML2

    UML中有3种构造块:事物.关系和图,事物是对模型中最具有代表性的成分的抽象:关系是把事物结合在一起:图聚集了相关的的事物.具体关系图标如下 说明:构件事物是名词,是模型的静态部分.行为事物是动态部分 ...

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

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

  8. 惊涛怪浪(double dam-break) -- position based fluids

    切入正题之前,先胡说八道几句.    据说爱因斯坦讲过:关于这个世界最难以理解的就是它是可以被理解的.人类在很长的时间里,都无法认知周围变幻莫测的世界,只能编造出无数的神祗来掌控世上万物的运行.到了近 ...

  9. ODBC连接问题

    http://zhidao.baidu.com/link?url=EPEMTuGC1q5wWavZigWseoHOwRLvpHyAVsdIgMLspErJOUZMEepIICUnT9IdkPQlYTm ...

  10. SNF开发平台WinForm之二-开发-单表表单管理页面-SNF快速开发平台3.3-Spring.Net.Framework

    2.1运行效果: 2.2开发实现: 2.2.1 这个开发与第一个开发操作步骤是一致的,不同之处就是在生成完代码之后,留下如下圈红程序,其它删除. 第一个开发地址:开发-单表表格编辑管理页面 http: ...