逝者如斯,不舍昼夜

尘世中一个迷途小书童,读书太少,想得太多
posts - 72, comments - 57, trackbacks - 0, articles - 0
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

栈的应用——表达式求值

Posted on 2016-11-28 14:27  SteveWang  阅读(...)  评论(... 编辑 收藏

 

  表达式求值是程序设计语言编译中的一个基本问题,它的实现就是对“栈”的典型应用。本文针对表达式求值使用的是最简单直观的算法“算符优先法”。

  本文给出两种方式来实现表达式求值,方式一直接利用中缀表达式求值,需要用到两个栈,操作数栈和操作符栈。首先置操作数栈为空栈, 操作符栈仅有“#”一个元素。依次读入表达式中的每个字符,若是操作数则进操作数栈,若是操作符则和操作符栈的栈顶运算符比较优先权作相应操作,直至整个表达式求值完毕。方式二首先把中缀表达式转换为后缀表达式并存储起来,然后利用读出的后缀表达式完成求值,其本质上是方式一的分解过程。

 

  表达式求值的代码如下:

#include <iostream>
#include "stack"
#include "map"
using namespace std;

/*   只能求一位整数的加减乘除混合运算   */

map<char, pair<int, int>> priority;    // 存放各个操作符的栈内栈外优先级,first是栈内,second是栈外
char infix[50];                        // 存放初始的中缀表达式
char postfix[50];                      // 存放转化的后缀表达式
int result;

void MakePriority()        // 构造运算符优先级表
{
    priority.insert(make_pair('#', make_pair(0, 0)));    // isp(#)=0, icp(#)=0
    priority.insert(make_pair('\n', make_pair(0, 0)));   // isp(\n)=0, icp(\n)=0  表达式结尾的'#'用'\n'代替,这样可以省略表达式末尾的结束符'#'
    priority.insert(make_pair('(', make_pair(1, 6)));    // isp(()=1, icp(()=6
    priority.insert(make_pair('*', make_pair(5, 4)));    // isp(*)=5, icp(*)=4
    priority.insert(make_pair('/', make_pair(5, 4)));    // isp(/)=5, icp(/)=4
    priority.insert(make_pair('%', make_pair(5, 4)));    // isp(%)=5, icp(%)=4
    priority.insert(make_pair('+', make_pair(3, 2)));    // isp(+)=3, icp(+)=2
    priority.insert(make_pair('-', make_pair(3, 2)));    // isp(-)=3, icp(-)=2
    priority.insert(make_pair(')', make_pair(6, 1)));    // isp())=6, icp())=1
}

void InfixToPostfix()        // 把中缀表达式转换为后缀表达式
{
    int i = 0;
    stack<char> optrStack;   // 操作符栈
    char optr;               // optr为栈顶的操作符
    optrStack.push('#');
    while (!optrStack.empty())
    {
        
        if (isdigit(infix[i]))  // 是操作数则直接输出(追加到postfix结尾)
        {
            postfix[strlen(postfix)] = infix[i];
            postfix[strlen(postfix) + 1] = '\0';
            i++;                // 读入中缀表达式的下一个字符
        }
        else                    // 是操作符, 比较优先级
        {
            optr = optrStack.top();    // 取出栈顶操作符
            if (priority[infix[i]].second > priority[optr].first)    // icp(infix[i]) > isp(optr),infix[i]入栈
            {
                optrStack.push(infix[i]);
                i++;
            }
            else if (priority[infix[i]].second < priority[optr].first)// icp(infix[i]) < isp(optr),optr退栈并输出
            {
                postfix[strlen(postfix)] = optr;
                postfix[strlen(postfix) + 1] = '\0';
                optrStack.pop();
            }
            else        // icp(infix[i]) = isp(optr),退栈但不输出,若退出的是'(',则继续读入下一个字符
            {
                optrStack.pop();
                if (optr == '(')
                    i++;
            }
        }
    }
}

void CalculateByPostfix()    // 通过后缀表达式求值
{
    int i = 0;
    stack<int> opndStack;    // 操作数栈
    int left, right;         // 左右操作数
    int value;               // 中间结果
    int newOpnd;
    while (postfix[i] != '#' && i < strlen(postfix))
    {
        switch (postfix[i])
        {
        case '+':
            right = opndStack.top();   // 从操作数栈中取出两个操作数
            opndStack.pop();
            left = opndStack.top();
            opndStack.pop();
            value = left + right;
            opndStack.push(value);     // 中间结果入栈
            break;
        case '-':
            right = opndStack.top();
            opndStack.pop();
            left = opndStack.top();
            opndStack.pop();
            value = left - right;
            opndStack.push(value);
            break;
        case '*':
            right = opndStack.top();
            opndStack.pop();
            left = opndStack.top();
            opndStack.pop();
            value = left * right;
            opndStack.push(value);
            break;
        case '/':
            right = opndStack.top();
            opndStack.pop();
            left = opndStack.top();
            opndStack.pop();
            if (right == 0)
            {
                cerr << "Divide by 0!" << endl;
            }
            else
            {
                value = left / right;
                opndStack.push(value);
            }
            break;
        default:
            newOpnd = (int)(postfix[i] - 48);  // 操作数直接入栈
            opndStack.push(newOpnd);
            break;
        }
        i++;
    }
    result = opndStack.top();
}

void CalculateByInfix()        // 直接利用中缀表达式求值
{
    int i = 0;
    stack<char> optrStack;     // 操作符栈
    stack<int>    opndStack;   // 操作数栈
    char optr;                 // optr为操作符栈顶的操作符
    int left, right, value;    // 左右操作数以及中间结果
    optrStack.push('#');
    optr = optrStack.top();
    while (!optrStack.empty())   // 直到操作符栈为空
    {

        if (isdigit(infix[i]))   // 是操作数, 进操作数栈
        {
            value = (int)(infix[i] - 48);
            opndStack.push(value);
            i++;
        }
        else                         // 是操作符, 比较优先级
        {
            optr = optrStack.top();  // 取出操作符栈顶的操作符
            if (priority[infix[i]].second > priority[optr].first)      // icp(infix[i]) > isp(optr),infix[i]入栈
            {
                optrStack.push(infix[i]);
                i++;
            }
            else if (priority[infix[i]].second < priority[optr].first) // icp(infix[i]) < isp(optr),optr退栈并输出
            {
                optrStack.pop();
                right = opndStack.top();    // 从操作数栈中取出两个操作数
                opndStack.pop();
                left = opndStack.top();
                opndStack.pop();
                switch (optr)
                {
                case '+':
                    value = left + right;
                    opndStack.push(value);   // 中间结果入栈
                    break;
                case '-':
                    value = left - right;
                    opndStack.push(value);   // 中间结果入栈
                    break;
                case '*':
                    value = left * right;
                    opndStack.push(value);   // 中间结果入栈
                    break;
                case '/':
                    if (right == 0)
                    {
                        cerr << "Divide by 0!" << endl;
                    }
                    else
                    {
                        value = left / right;
                        opndStack.push(value);
                    }
                    break;
                default:
                    break;
                }
            }
            else
            {
                optrStack.pop();
                if (optr == '(')
                    i++;
            }
        }
    }
    result = opndStack.top();
}


int main()
{
    MakePriority();    // 构造运算符优先级表

    cout << "请输入中缀表达式:";
    cin >> infix;

    cout << "直接利用中缀表达式求值为:";
    CalculateByInfix();
    cout << result << endl;

    cout << "转化为后缀表达式:";
    InfixToPostfix();
    for (int i = 0;i < strlen(postfix);i++)
    {
        cout << postfix[i];
    }
    cout << endl;

    cout << "利用后缀表达式求值为:";
    CalculateByPostfix();
    cout << result << endl;

    return 0;
}

 

  为了方便起见,本文只是简单的设计了一个针对一位整数的四则运算进行求值的算法,对于处理多位整数的四则运算,需要对本文接受输入的数据类型进行“升阶”,把字符数组换成字符串数组,将一个整数的多位数字存入一个字符串进行处理。