逆波兰表达式

  逆波兰表达式又叫做后缀表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式,解决了四则运算中括号改变运算符优先级的问题。
  四则运算的表达式一般都是中缀表达式,如1 + 2 * (3 - 4) + 5,即操作符在两个操作数之间。四则运算需要两个步骤,一是把中缀表达式转为后缀表达式,二是由后缀表达生成结果中缀表达式转为后缀表达式算法描述。

中缀到后缀的变换过程

  1. 所用数据结构:首先有个包含中缀表达式元素列表sourceList,然后创建一个符号列表destList保存最终后缀表达式,创建一个操作符堆栈opStack(作为由sourceList转为destList的中介)。
  2. 从sourceList取出一个元素A,如果是数字则加入到destList中;如果是运算符,将操作符A与操作符堆栈opStack栈顶的运算符的优先关系相比较。如果优先关系高于opStack栈顶的运算符,则将该运算符压入操作符堆opStack;倘若不是(低于或等于)的话,则将运算符栈opStack栈顶的运算符从栈中弹出保存到destList,重复此步骤,直到操作符A压入操作符堆栈opStack(对于+-来说,把opStack全部弹出,然后把A压入opStack中)。如果是左括号(,则压入操作符堆栈opStack(注意,(将之后的运算符的优先级与之前opStack中运算符的优先级隔开)。如果是右括号),则操作符堆栈opStack弹出操作符并加入到destList中,直到弹出左括号(
  3. 重复步骤2中的操作,所有元素处理完毕后将操作符堆栈opStack弹出操作符并加入到destList中,这样中缀式表示的简单算术表达式转化为逆波兰表达式所表示的简单算术表式。
  中缀表达式例如1 + 2 * (3 - 4) + 5,构造元素列表1 + 2 * ( 3 - 4 ) + 5,构造一个空最终后缀表达式destList,一个操作符堆栈opStack

  • 取出1,destList [ 1 ],opStack [ ]
  • 取出+,destList [ 1 ],opStack [ + ]
  • 取出2,destList [ 1 2 ],opStack [ + ]
  • 取出*,destList [ 1 2 ],opStack [ + * ]
  • 取出(,destList [ 1 2 ],opStack [ + * ( ]
  • 取出3,destList [ 1 2 3 ],opStack [ + * ( ]
  • 取出-,destList [ 1 2 3 ],opStack [ + * ( - ]
  • 取出4,destList [ 1 2 3 4 ],opStack [ + * ( - ]
  • 取出),destList [ 1 2 3 4 - ],opStack[ + * ]:操作符堆栈opStack弹出操作符并,加入到destList中,直到弹出左括号(
  • 取出+,destList [ 1 2 3 4 - * + ],opStack [ + ]:加号优先级不大于[ + * ]
  • 取出5,destList [ 1 2 3 4 - * + 5 ],opStack [ + ]
  • 处理完毕,destList [ 1 2 3 4 - * + 5 + ],opStack [ ]

用后缀表达式来计算结果

  遍历储存后缀表达式的列表,将元素依次进栈,当遇到操作符时,连续出栈两个元素,进行运算,再将结果进栈,最后栈内留下的元素就是计算结果。
  后缀表达式destList [ 1 2 3 4 - * + 5 + ],结果堆栈resultStatck [ ]格式为输入 -> 结果

[ 1 2 3 4 ] -> resultStatck [ 1 2 3 4 ]
[ - ]       -> resultStatck [ 1 2 3-4 ]
[ * ]       -> resultStatck [ 1 2*(3-4) ]
[ + ]       -> resultStatck [ 1+2*(3-4) ]
[ 5 ]       -> resultStatck [ 1+2*(3-4) 5 ]
[ + ]       -> resultStatck [ 1+2*(3-4)+5 ]

  要实现一个简单的计算器,可以对+ - * / ( )进行处理并返回正确的值,最先想到的就是逆波兰表达式。首先,用到第一个栈把算式转化为逆波兰表达式存一个数组中,在使用另一个栈对这个数组进行判断、出栈、运算、入栈、判断…,不断处理,最后的栈顶元素就是结果。

#include <iostream>
#include <stack>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

/* 把一个算式先转化为逆波兰表达式 */
int Priority ( char ch ) { /* 定义优先级别 */
    int i;

    switch ( ch ) {
        case '(': i = 1;  break;
        case '+': i = 2;  break;
        case '-': i = 2;  break;
        case '*': i = 4;  break;
        case '/': i = 4;  break;
        case ')': i = 5;  break;
        default : i = -1; break;
    }

    return i;
}

void tonibolan ( char *ch, char retch[100] ) {
    stack<char> st2;
    int i = 0;

    while ( *ch != '\0' ) {
        if ( *ch >= '0' && *ch <= '9' ) {
            retch[i++] = *ch;
        } else if ( *ch == '(' ) { /* 左括号直接压栈 */
            st2.push ( *ch );
        } else if ( *ch == ')' ) {
            while ( st2.top() != '(' ) {
                retch[i++] = st2.top();
                st2.pop();
            }

            if ( st2.top() == '(' ) {
                st2.pop();
            }
        } else if ( st2.empty() || Priority ( *ch ) > Priority ( st2.top() ) ) {
            st2.push ( *ch );
        } else {
            while ( Priority ( *ch ) <= Priority ( st2.top() ) ) {
                retch[i++] = st2.top();
                st2.pop();

                if ( st2.empty() ) {
                    break;
                }
            }

            st2.push ( *ch );
        }

        ch++;
    }

    while ( !st2.empty() ) {
        retch[i++] = st2.top();
        st2.pop();
    }
}

/* 计算逆波兰表达式的值 */
int calcval ( char *ret ) {
    stack<char> st;

    while ( *ret != '\0' ) {
        if ( *ret >= '0' && *ret <= '9' ) {
            st.push ( *ret );
        } else {
            switch ( *ret ) {
                case'+': {
                        char a = st.top();
                        st.pop();
                        char b = st.top();
                        st.pop();
                        st.push ( ( ( a - '0' ) + ( b - '0' ) + '0' ) );
                        break;
                    }

                case'-': {
                        char a = st.top();
                        st.pop();
                        char b = st.top();
                        st.pop();
                        st.push ( ( ( b - '0' ) - ( a - '0' ) ) + '0' );
                        break;
                    }

                case'*': {
                        char a = st.top();
                        st.pop();
                        char b = st.top();
                        st.pop();
                        st.push ( ( ( b - '0' ) * ( a - '0' ) ) + '0' );
                        break;
                    }

                case'/': {
                        char a = st.top();
                        st.pop();
                        char b = st.top();
                        st.pop();

                        if ( a != '0' ) {
                            st.push ( ( ( ( b - '0' ) / ( a - '0' ) ) + '0' ) );
                        } else {
                            cout << "除数为0错误" << endl;
                        }

                        break;
                    }
            }
        }

        ret++;
    }

    return st.top() - '0';
}

int main() {
    char ret[100] = {0};
    char ch[100] = {0};
    cin.get ( ch, 100 );
    tonibolan ( ch, ret );
    int len = sizeof ( ret ) / sizeof ( 0 );
    int i = 0;
    cout << "算式的逆波兰表达式为:" << endl;

    while ( len-- ) {
        cout << ' ' << ret[i++];
    }

    cout << "\n算式的计算结果为:" << endl;
    cout << calcval ( ret ) << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值