关于字符串表达式求值,应该是程序猿们机试或者面试时候常见问题之一,昨天参加国内某IT的机试,压轴便为此题,今天抽空对其进行了研究。

算术表达式中最常见的表示法形式有 中缀、前缀后缀表示法。中缀表示法是书写表达式的常见方式,而前缀和后缀表示法主要用于计算机科学领域。

中缀表示法
中缀表示法是算术表达式的常规表示法。称它为 中缀表示法是因为每个操作符都位于其操作数的中间,这种表示法只适用于操作符恰好对应两个操作数的时候(在操作符是二元操作符如加、减、乘、除以及取模的情况下)。对以中缀表示法书写的表达式进行语法分析时,需要用括号和优先规则排除多义性。

Syntax: operand1 operator operand2 Example: (A+B)*C-D/(E+F)

前缀表示法
前缀表示法中,操作符写在操作数的前面。这种表示法经常用于计算机科学,特别是编译器设计方面。为纪念其发明家 ― Jan Lukasiewicz(请参阅参考资料),这种表示法也称 波兰表示法

Syntax  : operator operand1 operand2 Example : -*+ABC/D+EF

后缀表示法
在后缀表示法中,操作符位于操作数后面。后缀表示法也称 逆波兰表示法(reverse Polish notation,RPN),因其使表达式求值变得轻松,所以被普遍使用。

Syntax  : operand1 operand2 operator Example : AB+C*DEF+/-

字符串表达式求值,一般来说采用如下方式:

一.  中缀表达式到后缀表达式的转换

要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须了解操作符的优先级和结合性。 优先级或者说操作符的强度决定求值顺序;优先级高的操作符比优先级低的操作符先求值。 如果所有操作符优先级一样,那么求值顺序就取决于它们的 结合性。操作符的结合性定义了相同优先级操作符组合的顺序(从右至左或从左至右)。

Left associativity  : A+B+C = (A+B)+C
Right associativity : A^B^C = A^(B^C)

转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:

  1. 初始化一个空堆栈,将结果字符串变量置空。
  2. 从左到右读入中缀表达式,每次一个字符。
  3. 如果字符是操作数,将它添加到结果字符串。
  4. 如果字符是个操作符,弹出(pop)操作符,直至遇见开括号(opening parenthesis)、优先级较低的操作符或者同一优先级的右结合符号。把这个操作符压入(push)堆栈。
  5. 如果字符是个开括号,把它压入堆栈。
  6. 如果字符是个闭括号(closing parenthesis),在遇见开括号前,弹出所有操作符,然后把它们添加到结果字符串。
  7. 如果到达输入字符串的末尾,弹出所有操作符并添加到结果字符串。

二. 后缀表达式求值

对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:

  1. 初始化一个空堆栈
  2. 从左到右读入后缀表达式
  3. 如果字符是一个操作数,把它压入堆栈。
  4. 如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
  5. 到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。

好了,基本思路讨论完毕,我们开始动手写代码,此段代码假设表达式中的预算符只包括四大基本运算符+、-、*、/,为了简化代码,我们也假设表达式中的数字只包括1-9。

函数getPostfixExp用来将一个中缀表达式转换为后缀表达式(也就是逆波兰式).

string getPostfixExp(string& infix)
{
stack<char> operator_stack;
string postfix;
for (auto p : infix)
{
if (isOperator(p))
{
while (!operator_stack.empty() && isOperator(operator_stack.top()) && priority(operator_stack.top()) >= priority(p))
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
operator_stack.push(p);
}
else if (p == '(')
{
operator_stack.push(p);
}
else if (p == ')')
{
while (operator_stack.top() != '(')
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
operator_stack.pop();
}
else
{
postfix.push_back(p);
postfix.push_back(' ');
}
}
while (!operator_stack.empty())
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
postfix.pop_back();
return postfix;
}

其中isOperator函数如下:

bool isOperator(char ch)
{
switch(ch)
{
case'+':
case'-':
case'*':
case'/':
return true;
default:
return false;
}
}

其中的priority函数如下:

int priority(char a) {
int temp;
if (a == '*' || a == '/')
temp = ;
else if (a == '+' || a == '-')
temp = ;
return temp;
}

得到了后缀表达式,开始我们的求值之旅吧!

int postfixCalculate(string& postfix)
{
int first, second;
stack<int> num_stack;
for (auto p : postfix)
{
switch (p)
{
//if the item is an operator (+, -, *, or /) then // pop two numbers off the stack // make a calculation: the second number
// popped-operator-first number // push the result on the stack
case '*':
getTwoNums(num_stack, first, second);
num_stack.push(first * second);
break;
case '/':
getTwoNums(num_stack, first, second);
num_stack.push(first / second);
break;
case '+':
getTwoNums(num_stack, first, second);
num_stack.push(first + second);
break;
case '-':
getTwoNums(num_stack, first, second);
num_stack.push(first - second);
break;
case ' ':
break;
// if the item is a number push it on the stack
default:
num_stack.push(p - '');
break;
}
}
int result = num_stack.top();
num_stack.pop();
return result;
}

其中getTwoNums函数如下:

void getTwoNums(stack<int>& num_stack, int& first, int& second)
{
second = num_stack.top();
num_stack.pop(); first = num_stack.top();
num_stack.pop();
}

好了,全部的代码结束了,写个main函数试试吧!

int main()
{
string infix;
cin >> infix;
string postfix = getPostfixExp(infix);
cout << postfix << endl;
cout << postfixCalculate(postfix) << endl;
system("PAUSE");
return ;
}

写在最后的话:

字符串表达式求值方法很多,本文中利用stack结合优先级的方式,解决了这个问题。其他的方法,有表达式树的方式,编译原理的书上有讲解,大家可以结合原理,自己动手实现生成表达式树的代码,然后求值就变得so easy了,当然也有与上面两者迥然不同的方式,大家举一反三,多研究!

C++之字符串表达式求值的更多相关文章

  1. CF552E 字符串 表达式求值

    http://codeforces.com/contest/552/problem/E E. Vanya and Brackets time limit per test 1 second memor ...

  2. Java 计算数学表达式(字符串解析求值工具)

    Java字符串转换成算术表达式计算并输出结果,通过这个工具可以直接对字符串形式的算术表达式进行运算,并且使用非常简单. 这个工具中包含两个类 Calculator 和 ArithHelper Calc ...

  3. NYOJ 1272 表达式求值 第九届省赛 (字符串处理)

    title: 表达式求值 第九届省赛 nyoj 1272 tags: [栈,数据结构] 题目链接 描述 假设表达式定义为: 1. 一个十进制的正整数 X 是一个表达式. 2. 如果 X 和 Y 是 表 ...

  4. nyoj305_表达式求值

    表达式求值 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min ...

  5. 数据结构--栈的应用(表达式求值 nyoj 35)

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=35 题目: 表达式求值 时间限制:3000 ms | 内存限制:65535 KB描述 AC ...

  6. 【算法】E.W.Dijkstra算术表达式求值

    算术表达式求值 我们要学习的一个栈的用例同时也是展示泛型的应用的一个经典例子,就是用来计算算术表达式的值,例如 ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ) 如果将4乘以5,把3 ...

  7. 【NYOJ-35】表达式求值——简单栈练习

    表达式求值 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min ...

  8. 表达式求值(河南省第四届ACM试题-C题)题解

    以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定指定的一个由3种函数组成的表达式,计算其数值. [题目分析] 一开始以为是后缀表达式,后来抽了没想出来,最后用了递归的方法解 ...

  9. java实现算术表达式求值

    需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...

随机推荐

  1. Sencha Touch和jQuery Mobile的比较

    第一组-行销和平台支持 Sencha Touch和jQuery Mobile都以HTML5框架著称.jQuery Mobile谦虚的说自己只是内建于所有流行的移动设备平台,而Sencha Touch则 ...

  2. mongodb复制集里查看主从操作日志oplog

    MongoDB的replica set架构是通过一个日志来存储写操作的,这个日志就叫做 oplog .oplog.rs 是一个固定长度的 Capped Collection,它存在于local数据库中 ...

  3. WPF中给Button加上图标和文字

    要实现在Button里面加入图标或者图形以及文字,我们就需要在Button里面用一个WrapPanel控件,这个WrapPanel控件会把我们的图标或者文字进行包裹,并显示出来. Xaml: < ...

  4. 换个语言学一下 Golang (5)——运算符

    运算符用于在程序运行时执行数学或逻辑运算. Go 语言内置的运算符有: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 接下来让我们来详细看看各个运算符的介绍. 算术运算符 下表 ...

  5. 数组初始化 和 vector初始化

    ] = {}; 整个数组都初始化为0 vector<); 整个vector初始化为1 如果你定义的vector是这样定义的: vector<int> B; 去初始化,千万不要用: ; ...

  6. C++ static关键字

    一.面向过程中的static 1.修饰全局变量(静态全局变量) (1)静态全局变量在全局数据区分配内存: (2)未经初始化的静态全局变量会被程序自动初始化为0: (3)静态全局变量在申明它的整个文件是 ...

  7. 第2节 azkaban调度:17、azkaban的两个服务模式的安装

    2.3.3.azkaban两个服务模式安装 1.确认所需软件: Azkaban Web服务安装包 azkaban-web-server-0.1.0-SNAPSHOT.tar.gz Azkaban执行服 ...

  8. Mysql插入中文时提示:ERROR 1366 (HY000): Incorrect string value: '\xE5\x8F\xB0\xE5\xBC\x8F...' fo

    Mysql插入数据时提示:ERROR 1366 (HY000): Incorrect string value: ‘\xE5\x8F\xB0\xE5\xBC\x8F…’ fo 分析如下: 首先通过语句 ...

  9. NSLayoutConstraint.constraintsWithVisualFormat详解,以及AlignAllCenterY

    NSLayoutConstraint.constraintsWithVisualFormat详解,以及AlignAllCenterY 转载2015-07-08 18:02:02 鉴于苹果官方文档的解释 ...

  10. 【贪心优化dp决策】bzoj1571: [Usaco2009 Open]滑雪课Ski

    还有贪心优化dp决策的操作…… Description Farmer John 想要带着 Bessie 一起在科罗拉多州一起滑雪.很不幸,Bessie滑雪技术并不精湛. Bessie了解到,在滑雪场里 ...