前置知识
栈
- 定义
只允许在一端进行插入或者删除的线性表
- 基本概念
1.栈顶:线性表允许进行插入删除的一端。
2.栈底:不允许进行插入和删除的一端。
3.空栈:不包含任何元素的空表。
栈又称先进后出(last in first out)的线性表,简称为LIFO结构
c++基本函数介绍:
- 头文件
#include <stack>
- 创建栈
std::stack<DataType> myStack;
//这里创建一个实例对象
stack<int> st;
- 判断栈是否为空栈
如果栈为空返回true,否则返回false
st.empty();
- 压栈(入栈)
向栈内压入一个成员
st.push(parameter); //parameter为形参,为栈内元素类型
- 出栈
从栈顶弹出一个成员
st.pop(); //要注意栈内有元素,即不为空
- 返回栈顶
返回栈顶,但不删除成员
st.top(); //注意栈内要有元素,不能为空
- 栈大小
返回栈内元素的大小
st.size();
中缀表达式(中缀记法)
- 定义
操作符以中缀形式处于操作数的中间,
形如 2*(3+4) 这样的表达式。
对计算机来说中缀表达式却是很复杂的,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式,然后再进行求值。
前缀表达式(前缀记法、波兰式)
- 定义
操作符以中缀形式处于操作数的中间
将中缀表达式2*(3+4)转换为前缀表达式则为* 2 + 3 4
- 计算机的求值方法
从右到左扫描表达式
遇到数字时,将数字压入堆栈
遇到运算符时,弹出栈顶的两个数,用运算符对他们进行相应的计算,并将结果入栈
重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
后缀表达式(后缀记法、逆波兰式)
- 定义
后缀表达式与前缀表达式类似,只是运算符位于操作数之后
- 计算机的求值方法
从左到右扫描表达式
遇到数字时,将数字压入堆栈
遇到运算符时,弹出栈顶的两个数,用运算符对它们进行相应的计算,并将结果入栈
重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
因为后缀表达式拥有和前缀表达式一样的特点,并且计算顺序更符合习惯,故一般使用较多
算法设计
后缀表达式在计算机的运算逻辑是:如1 2 + ,我们会将运算符前的俩个数字进行该运算操作。
后缀表达式没有运算符优先级和括号,所以我们必须判断运算符的优先级,然后按照运算顺序将运算符排列。针对带有括号最优先计算,处理时要另外考虑。
我们针对不同性质的符号,分为三类:
一种是数字,我们读到就可以直接加在输出的字符串的尾端;
一种是运算符,我们需要设置优先级来依次加在字符串的尾端,我们需要用栈来临时保存运 算符,来判断哪个运算符先加在尾端。
栈空的时候,我们需要压栈来确保运算符在数字后面并且可以用来与其他运行符比较;
运算符高的先运算,所以在后缀表达式中,一般是优先级高的会比优先级低的运算符位置靠前;
一种是括号,括号里面的运算会高于括号外的,所以遇到括号时,要考虑到:
当遇到左括号时,压入栈中,我们会以此为界限,处理括号里面的中缀表达式,将其转化为后缀表达式插入到括号外的后缀表达式作为一个运算的数字(括号内的表达式运算结束会计算出数字,因此在整个后缀表达式中作为数字参与运算)。然后当遇到右括号时,我们需要将栈里面的运算符出栈直到左括号出栈,这样我们就完成了括号里面的转换。
例:
http://t.csdnimg.cn/jwozKhttp://t.csdnimg.cn/jwozK
前缀表达式:a+b*c+(d*e+f)*g
后缀表达式:abc*+de*f+g*+
代码实现
qt
最近使用qt做一个计算器,于是这一段代码就贴qt分格的代码
- 中缀转后缀
QString Widget::inToPost(QString infix)throw(const char*)
{
QStack<QChar> st;
QString postfix;
QMap<QChar, int> priority;
//设置运算符优先级
priority['+'] = 0;
priority['-'] = 0;
priority['*'] = 1;
priority['/'] = 1;
priority['^'] = 2;
int count = 0;
int flag = 0;
for(QChar c : infix){
++count;
//c是数字
if (c.isDigit())
{
postfix.push_back(c);
continue;
}
//c是运算符
if (priority.contains(c))
{
//分割运算符数,但更准确来说,是将字符串里的数字分割出来
//因为数字有些会有很多位,我们每次只读字符串中的一位
if(flag == 0){
postfix.push_back('#');
}
if(flag == 1){
flag = 0;
}
//对于栈空和左括号处理
if(st.isEmpty() || st.top() == '('){
st.push(c);
continue;
}
//当栈不空时,我们就需要处理当前和栈中运算符的优先级
if (!st.isEmpty())
{
//将c和栈顶运算符优先级比较
//若大于则压入栈中结束,否则将栈顶运算符弹出加入postfix末尾并重复上一步
while (priority.value(c) <= priority.value(st.top()))
{
postfix.push_back(st.top());
st.pop();
}
if(priority.value(c) > priority.value(st.top()))
{
st.push(c);
}
}
}
//其他单位字符%或.
if(!priority.contains(c)){
if(c == '.'){
postfix.push_back(c);
}
if (c == '(')
{
st.push(c);
}
if (c == ')')
{
flag = 1;
//标识运算数结束
postfix.push_back('#');
//将栈中的运算符依次加入postfix末尾,直到遇到左括号
while(st.top() != '(')
{
postfix.push_back(st.top());
st.pop();
}
//弹出'('
st.pop();
}
}
}
//如果最后一位是数字的话,我们需要标记数字的结束
if(count == postfix.length() && postfix[postfix.length() - 1].isDigit())
postfix.push_back('#');
//确保运算符都添加到后缀
while(!st.isEmpty()){
postfix.push_back(st.top());
st.pop();
}
return postfix;
}
- 计算后缀表达式
double Widget::compute(QString postfix)throw(const char*)
{
qDebug()<<postfix<<endl;
std::stack<QString> st;
double num1, num2;
QString str;
QMap<QChar, int> priority;
//设置运算符优先级
priority['+'] = 0;
priority['-'] = 0;
priority['*'] = 1;
priority['/'] = 1;
priority['^'] = 2;
for (QChar c : postfix)
{
//遍历postfix计算;
if (c.isDigit() || c == '.')
{
str.push_back(c);
}
if (c == '#')
{
//将str转化为double类型并压入st中
st.push(str);
str = "";
}
if (priority.contains(c))
{
num1 = st.top().toDouble();st.pop();
num2 = st.top().toDouble();st.pop();
//根据运算符种类计算并将结果压入st;
if(c == '+'){
st.push(QString::number(num1+num2));
}
if(c == '-'){
st.push(QString::number(num2-num1));
}
if(c == '*'){
st.push(QString::number(num1*num2));
}
if(c == '/'){
st.push(QString::number(num2/num1));
}
if(c == '^'){
st.push(QString::number(qPow(num2,num1)));
}
}
}
return st.top().toDouble();
}