后缀式的特点可用于计算表达式的值,我们通过中缀转后缀的方式可以将原表达式重新排列组合成为计算机易于理解的计算方式。
详情请参考:逆波兰算法(后缀表达式)
关于中缀转后缀
中缀式转后缀式 如:
1+2就是一个中缀式,转换成后缀式为1 2 +
1+2*3…… ……1 2 3 * +
1+(2+5)…… ……1 2 5 + +
在计算时,从最左侧开始先选定一个符号与该符号左边的两个数结合运算。以下(黄色高亮部分)计算过程如下
- 1+2…… ……1 2 +
1与2进行+运算
- 1+2*3…… ……1 2 3 * +
1. 最左边的符号 * ,左侧的两个数 2 3
计算:2*3 = 6
表达式:1 6 +
2. 最左边的符号 + ,左侧的两个数 1 6
计算:1+6 = 7
- 1+(2+5)…… ……1 2 5 + +
3. ...
计算:2+5=7
表达式:1 7 +
4. ...
计算:1+7=8
如上所示,使用后缀式可以清晰的表示出表达式中的运算关系,所有运算符符号中,优先级最高的在最左边。
计算时从最左侧的符号先开始,且每个符号都与它左侧最近的两个数据结合。
可以说,后缀式就是根据计算表达式中的优先级进行设计的。
比如,在计算表达式中 ’ ( ) ’ 的优先级最高,因此在转后缀式时遇到 ‘(’ 与 ‘)’ 之间的符号就认为是优先级最高的符号。
eg: a*(b+c)
a b c + *
过程详解:
1、===a*(b+c)===
↑
后缀: a ✔
符号:
2、===a*(b+c)===
↑
后缀: a
符号: * ✔
3、===a*(b+c)===
↑
后缀: a
符号: * ( ✔
4、===a*(b+c)===
↑
后缀: a b ✔
符号: * (
5、===a*(b+c)===
↑
后缀: a b
符号: * ( + ✔
6、===a*(b+c)===
↑
后缀: a b c ✔
符号: * ( +
7、===a*(b+c)===
↑
后缀: a b c
符号: * ( + ) ✔
注:遇到')',后面进来的符号都视为较低优先级符号,因此把' ( ) '
内的符号全部转移到后缀式中
8、=============
后缀: a b c + ✔
符号: *
9、=============
后缀: a b c + * ✔
符号:
中缀转后缀技巧
中缀转后缀的核心就是,将优先级高的运算放在最前边计算,如 … (b+c)… 形式的表达式 ,要变成 … b c + … 。后缀式中优先级体现在符号自左向右的顺序,表达式的关系体现在每个符号与其左边最近的两个数字。
而针对表达式 a+b-c ,因为 ‘+’ ‘-’ 优先级相同,而我们的计算顺序为同优先级自左向右,则我们认为左边的 ‘+’ 优先级更高。因此后缀式为 a b + c - ,其中 a b + 为(a+b) 就是原式中优先级高的部分。
而对于 a + b * c 的表达式,显然 b*c 的优先级最高,则后缀式为 a b c * + 。
在本例中,函数 void InfixToSuffix();
进行中缀转后缀计算。
中缀转后缀计算机代码
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template<typename T>
class Stack
{
public:
Stack(int _m_size = 20) :m_size(_m_size), mtop(0)
{
data = new T[m_size]();
}
~Stack()
{
delete[] data;
}
/* 判空 */
bool empty()
{
return mtop == 0;
}
void push(int val)
{
if (Full())
{
throw std::exception("error: Stack is Full !");
}
data[mtop] = val;
mtop++;
}
T top()
{
if (empty())
{
throw std::exception("error: The stack is empty !");
}
return data[mtop - 1];
}
void pop()
{
if (empty())
{
throw std::exception("error: Stack is Empty !");
}
mtop--;
}
void show()
{
if (empty())
{
cout << endl;
}
else
{
int i = 0;
while (i < mtop)
{
cout << data[i] << " ";
i++;
}
cout << endl;
}
}
private:
bool Full()
{
return mtop == m_size;
}
T* data;
int mtop;
int m_size;
};
/* 计算器类 */
class Calculator
{
public:
// 这里m_size 默认给的是40,m_size限制表达式的长短
Calculator(int _m_size = 40) :m_size(_m_size), m_result(0)
{
m_str = new char[_m_size + 1]();
}
~Calculator()
{
delete[] m_str;
}
/* 输入 */
void setData()
{
cin.getline(m_str, m_size - 1); //最多输入m_size -1个,末尾加'/0'
}
/* 中缀转后缀 */
void InfixToSuffix(); /* 把m_str输入的中缀式转换为后缀式 */
/* 后缀计算 */
void Compure();
/* 计算结果 */
double Getm_result()
{
return m_result;
}
private:
bool IsPop(char a, char b);
char* m_str;
double m_result;
int m_size;
};
int main()
{
while (1) {
Calculator ca;
ca.setData();
ca.InfixToSuffix();
ca.Compure();
cout << ca.Getm_result() << endl;
}
return 0;
}
// 比较符号优先级,是否出符号栈
// 栈顶元素b的优先级是否高于符号a, true/false 出栈b/入栈a
bool Calculator::IsPop(char a, char b) //a为待比较符号,b为符号栈栈顶元素
{
if (b == '(') return false; //‘(’优先级最低
if (a == '*' || a == '/')
{
if (b == '+' || b == '-')
{
//可以入符号栈 // 栈外优先级高,可以继续入栈
return false;
}
else
{
return true;
}
}
//if (a == '+' || a == '-')
//{
//a符号为最低优先级,符号栈出栈
return true;
//}
}
/* 中缀转后缀 入栈 */
void Calculator::InfixToSuffix()
{
// 转换为后缀式后,为了区分数字:如5、4……,在原来的数据增加了分隔符
// dst 中存储输入的表达式的后缀式形式 size*2
char* dst = new char[m_size*2]();
/* 符号栈 */
Stack<char> symbol; // 在转后缀的过程中,(通过符号优先级)控制符号在后缀式中的位置
int i = 0;
int k = 0;
while (m_str[i] != '\0')
{
/*----------- 特殊符号判断------------------------------*/
if (m_str[i] == ' ') //如果当前字符是空格,则往后走一个
{
i++;
continue;
}
else if (m_str[i] == '(') //左括号直接入栈
{
symbol.push(m_str[i]);
}
else if (m_str[i] == ')') //遇到 ) ,输出( )之间的所有符号
{
while (symbol.top() != '(')
{
dst[k++] = symbol.top();
dst[k++] = ' ';
symbol.pop();
}
symbol.pop();
}
/*----------------- 数字 -------------------------------*/
else if (isdigit(m_str[i]))
{
dst[k++] = m_str[i];
if (!isdigit(m_str[i + 1])) //数字后加空格
{
dst[k++] = ' ';
}
}
/*----------------- 运算符 -------------------------------*/
else
{
switch (m_str[i])
{
case '+':case '-':case '*':case '/':
if (symbol.empty()) //如果符号栈为空,直接入符号
{
symbol.push(m_str[i]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
// 栈内符号优先级高于栈外,出栈。 IsPop == true;
if (IsPop(m_str[i], symbol.top())) //出符号栈
{
dst[k++] = symbol.top();
symbol.pop();
continue;
}
else //当前符号优先级高,入符号栈
{
symbol.push(m_str[i]);
}
}
break;
default:
break;
}
}
i++; /* 遍历下一字符 */
}
/*字符串已遍历完,把符号栈中剩余的符号入栈到数字栈中 */
while (!symbol.empty())
{
dst[k++] = symbol.top();
symbol.pop();
}
dst[k] = '\0';
// 输出后缀式
//cout << dst << endl;
// 后缀表达式 保存到 Calculator::m_str中
// 交换dst 与 m_str 的堆区空间
char* tmp = dst;
dst = m_str;
m_str = tmp;
delete[]dst;
}
void Calculator::Compure()
{
/* 辅助栈 */
Stack<double> st;
int i = 0;
while (m_str[i] != '\0')
{
/*----------- 特殊符号判断------------------------------*/
if (m_str[i] == ' ') //如果当前字符是空格,则往后走一个
{
i++;
continue;
}
/*----------------- 数字 -------------------------------*/
else if (isdigit(m_str[i]))
{
double tmp = 0;
while (m_str[i] != ' ')
{
tmp = tmp * 10 + m_str[i] - '0';
i++;
}
st.push(tmp);
}
/*----------------- 运算符 -------------------------------*/
else if (!st.empty()) //非空
{
double tmp = 0;
switch (m_str[i])
{
case '+':
tmp += st.top();
st.pop();
tmp += st.top();
st.pop();
st.push(tmp);
break;
case '-':
tmp -= st.top();
st.pop();
tmp += st.top();
st.pop();
st.push(tmp);
break;
case '*':
tmp = st.top();
st.pop();
tmp *= st.top();
st.pop();
st.push(tmp);
break;
case '/':
{
tmp = st.top();
st.pop();
if (tmp != 0)
{
tmp = st.top() / tmp;
st.pop();
st.push(tmp);
}
else
{
throw std::exception("error: Divisor of 0 !");
}
}
break;
default:
break;
}
}
i++; /* 遍历下一字符 */
}
this->m_result = st.top();
st.top();
}
运行截图:
注:在计算器类中
Calculator(int _m_size = 40) :m_size(_m_size), m_result(0)
{}
构造函数 _m_size 默认设置的为40,这将影响到我们输入的计算表达式的长度,我们在编译时可以根据需求自行更改代码。