关于字符串表达式求值,应该是程序猿们机试或者面试时候常见问题之一,昨天参加国内某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. 初探ant design pro

    1.增加路由子页面&配置菜单 因为ant design pro采取的是umi路由配置,所以只要在对应的文件夹下新建相关的文件夹以及文件,它会自动解析.按照如下的步骤做即可 PS.如果想要给菜单 ...

  2. Linux中配置系统参数

    [root@localhost ~]# vim /etc/security/limits.conf root soft nofile 65535root hard nofile 65535* soft ...

  3. iOS7.1企业应用"无法安装应用程序 因为证书无效"的解决方案

    今天升级了iOS7.1后发现通过之前的url无法安装企业应用了,一直提示“无法安装应用程序 因为http://xxx.xxx.xxx证书无效”,折腾了一番,终于在StackOverFlow上找到了答案 ...

  4. QT5:先导篇 全局定义

    一.简介 <QtGlobal>头文件包含了Qt类库的一些全局定义,包含基本数据类型 函数和宏 二.全局变量定义 <QtGlobal>定义的数据类型: Qt数据类型        ...

  5. snprintf()返回值的陷阱

    int snprintf(char *restrict buf, size_t n, const char * restrict  format, ...); 函数说明:最多从源串中拷贝n-1个字符到 ...

  6. java在线聊天项目 客户端登陆窗口LoginDialog的注册用户功能 修改注册逻辑 增空用户名密码的反馈 增加showMessageDialog()提示框

    LoginDialog类的代码修改如下: package com.swift.frame; import java.awt.EventQueue; import java.awt.event.Acti ...

  7. TCP/IP各种数据包结构体

    下面这些TCP/IP数据包是我在进行Socket及Wipcap网络编程过程中曾经用到过的数据包结构体, 这些东西平时看起来不起眼,真正用到的时候就会觉得非常有用...... 以太帧头格式结构体,共14 ...

  8. baidumap demo(一)

    覆盖物概述 地图上自定义的标注点和覆盖物我们统称为地图覆盖物.您可以通过定制BMKAnnotation和BMKOverlay来添加对应的标注点和覆盖物.地图覆盖物的设计遵循数据与View分离的原则,B ...

  9. http post get 同步异步

    下面首先介绍一下一些基本的概念---同步请求,异步请求,GET请求,POST请求. 1.同步请求从因特网请求数据,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作.也 ...

  10. 关于“xx.xx已被OS X使用,无法打开”的问题

    Mac电脑上的文件拷贝到移动硬盘,有时候会出现“xx.xx已被OS X使用,无法打开”的问题. 解决办法: 1.打开终端(或在Mac搜索里面输入Terminal); 2.在终端里输入 xattr -l ...