后缀表达式 / 逆波兰表达式
运算表达式:
- a + b:中缀表达式 原因是运算符号在两个运算对象的中间。
+ - a * b c d:前缀表达式 运算符在运算对象前面, 又称为波兰表达式。
a b c * - d +:后缀表达式运算符在运算对象后面, 又称为逆波兰表达式(RPN,Reversed Polish Notation)。
后缀表达式的优点 :
- 1. 相较于前缀表达式更易于转换,最左边一定为数字。
- 2. 不用括号,依靠运算顺序确定运算符的优先级。
- 3 .更符合计算机的计算方式。计算机通过从左至右读取后缀表达式,就可以将遇到的运算对象压入栈,在遇到运算符时就弹出两个运算对象,完成计算,再将结果压入栈。最后留在栈中的就是计算结果。
下面我就举一个最经典的例题:
后缀表达式 / 逆波兰表达式求解:
思想:
将后缀表达式存入数组,然后一一遍历。遇到数字(OP_NUM)压入栈,遇到运算符即符号(OP_SYMBOL)则将栈中的栈顶前两个元素出栈,另外栈顶元素是右操作数。计算出的结果继续压栈,直到整个数组遍历完,最后栈中剩余唯一的数就是计算结果。
代码实现:
#pragma once
#include <iostream>
using namespace std;
#include <vector>
#include <stack>
enum OP_TYPE
{
OP_SYMBOL,// 操作符
OP_NUM, // 操作数
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
};
struct Cell
{
OP_TYPE _type;//类型 : OP_SYMBOL操作符 OP_NUM操作数
int _value;//OP_SYMBOL的值都有:OP_ADD OP_SUB OP_MUL OP_DIV
};
class RPN
{
public:
RPN(Cell* arr,size_t n)
{
_rpn.reserve(n);//开一个n大小的空间,不初始化
for(size_t i = 0; i < n; ++i)
{
_rpn.push_back(arr[i]);
}
}
int count()
{
stack<int> s;
for(size_t i=0; i < _rpn.size() ; ++i)
{
if(_rpn[i]._type == OP_NUM)// 如果类型是数字的话,就入栈
{
s.push(_rpn[i]._value);
}
else if(_rpn[i]._type == OP_SYMBOL)//如果是操作符,就出栈计算
{
// 两个操作数出栈并且计算后将结果入栈
//出栈
int right = s.top();
s.pop();
int left = s.top();
s.pop();
//计算并将结果入栈
switch(_rpn[i]._value)
{
case OP_ADD:
s.push(left+right);
break;
case OP_SUB:
s.push(left-right);
break;
case OP_MUL:
s.push(left*right);
break;
case OP_DIV:
s.push(left/right);
break;
default:
break;
}
}
}
return s.top();
}
protected:
vector<Cell> _rpn;
};
void TestRPN()
{
Cell arr[] = {
{OP_NUM, 12},
{OP_NUM, 3},
{OP_NUM, 4},
{OP_SYMBOL, OP_ADD},
{OP_SYMBOL, OP_MUL},
{OP_NUM, 6},
{OP_SYMBOL, OP_SUB},
{OP_NUM, 8},
{OP_NUM, 2},
{OP_SYMBOL, OP_DIV},
{OP_SYMBOL, OP_ADD},
};
RPN rpn(arr, sizeof(arr)/sizeof(Cell));
cout<<"计算结果:"<<rpn.count()<<endl;
}
int main()
{
TestRPN();
return 0;
}