栈应用之逆波兰表达式与表达式求值

本文介绍如何通过编程实现从普通的中缀表达式到逆波兰表达式的转换,并利用逆波兰表达式进行高效的表达式求值。文章提供具体的算法实现细节及示例代码。

逆波兰表达式
所谓逆波兰表达式是指先操作数后操作符的后缀表达式,比如中缀表达式3X4的逆波兰为3 4 X,这是简单的逆波兰表达式,稍微复杂一点的比如3X(5+4)-6的逆波兰表达式为3 5 4 + X 6 -,任何算术表达式都可以转换为逆波兰表达式,在这里为了方便就用简单的表达式来举例了。我们可以用程序来完成中缀表达式到逆波兰表达式的转换。
中缀到逆波兰表达式的转换
给出如下程序来完成转换:

int Icp(char ch)
{
    if (ch == '(')
        return 4;
    else if (ch == '*' || ch == '/' || ch == '%')
        return 3;
    else if (ch == '+' || ch == '-')
        return 2;
    else
        return 1;
}

bool Judge(char ch)
{
    if ((ch >= '0'&&ch <= '9') || ch == '+' || ch == '-' || ch == '*' ||
        ch == '/' || ch == '%' || ch == '(' || ch == ')')
        return true;
    return false;
}

void ChangeNibolan(const char *crt)
{
    assert(crt != NULL);
    int len = strlen(crt);
    Stack<char> stack_ch;
    for (int i = 0; i < len; i++)
    {
        if (Judge(crt[i]))
        {
            if (crt[i] >= '0' && crt[i] <= '9')
                cout << crt[i] << " ";
            else
            {
                if (stack_ch.Empty() || (Icp(stack_ch.Top()) < Icp(crt[i])) ||
                    ((Icp(stack_ch.Top()) > Icp(crt[i]))&&(stack_ch.Top() == '(')))
                    stack_ch.Push(crt[i]);
                else if (Icp(stack_ch.Top()) >= Icp(crt[i]) && crt[i] != ')')
                {
                    while ( (!stack_ch.Empty()) && ((Icp(stack_ch.Top() >= Icp(crt[i]))) && stack_ch.Top() != '(' ))
                    {
                        cout << stack_ch.Top() << " ";
                        stack_ch.Pop();
                    }
                    stack_ch.Push(crt[i]);
                }
                else if (crt[i] == ')')
                {
                    cout << stack_ch.Top() << " ";
                    stack_ch.Pop();
                    stack_ch.Pop();
                }
            }
        }
        else
            return;
    }
    while (!stack_ch.Empty())
    {
        cout << stack_ch.Top() << " ";
        stack_ch.Pop();
    }
    cout << endl;
}

代码中Icp函数用于判断运算符的优先级,Judge函数用于判断字符是否是运算符或者括号,最后一个函数则是程序的主题。我们用一个简单的表达式来测试一下:

void test3()
{
    char array[] = "3*(4*(5+6*2-7)-8)+9-8/2";  //逆波兰表达式为:3 4 5 6 2 * + 7 - * 8 - * 9 + 8 2 / -
    ChangeNibolan(array);
}

看看结果:
这里写图片描述
显然成功转换。

表达式求值
用程序来进行表达式求值有多种方法其中用栈进行求值常用有两种,一种用两个栈,分别存储运算数和运算符,这种比较简单,在此不作实现。另一种用一个栈,结合逆波兰表达式进行求值,这种方法原理也简单,但是要用到逆波兰表达式,因此这里就用这种方法来实现。
先给出如下结构:

enum OPERATOR
{
    DEFAULT,
    ADD,
    SUB,
    MUL,
    DIV
};

struct Cell
{
    int data;
    OPERATOR OP;
};

枚举结构中给出四则运算和一个默认值,结构体中存储运算数或者运算符,再给出这样一个数组:

//"5*(3+4)-6+8/2"
    Cell PRN[] = { {5,DEFAULT},{3,DEFAULT},{4,DEFAULT},{0,ADD}
        ,{0,MUL},{6,DEFAULT},{0,SUB},{8,DEFAULT},{2,DEFAULT},{0,DIV},{0,ADD} };

这个数组就是我们要进行运算的表达式,这样看起来比较麻烦,但其实我们用上面实现的逆波兰表达式转换程序可以很容易就将一个中缀表达式转换为这样的一个逆波兰表达式数组,下面给出运算程序:

int CalcRPN(Cell* RPN, int size)
{
    assert(RPN != NULL);
    Stack<int> stack_cell;
    for (int i = 0; i < size; i++)
    {
        if (RPN[i].OP == DEFAULT)
        {
            stack_cell.Push(RPN[i].data);
        }
        else
        {
            assert(stack_cell.Size() > 1);
            int right = stack_cell.Top();
            stack_cell.Pop();
            int left = stack_cell.Top();
            stack_cell.Pop();
            switch (RPN[i].OP)
            {
            case ADD:
                stack_cell.Push(left + right);
                break;
            case SUB:
                stack_cell.Push(left - right);
                break;
            case MUL:
                stack_cell.Push(left*right);
                break;
            case DIV:
                assert(right != 0);
                stack_cell.Push(left / right);
                break;
            default :
                break;
            }
        }
    }
    return stack_cell.Top();
}

测试代码:

void test2()
{
    Cell PRN[] = { {5,DEFAULT},{3,DEFAULT},{4,DEFAULT},{0,ADD}
        ,{0,MUL},{6,DEFAULT},{0,SUB},{8,DEFAULT},{2,DEFAULT},{0,DIV},{0,ADD} };
    int sz = sizeof(PRN) / sizeof(PRN[0]);
    cout << CalcRPN(PRN, sz) << endl;
}

运行看看求值是否正确:
这里写图片描述

结果正确!
程序还可以改进,欢迎各位高手斧正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值