找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 459|回复: 1

[分享] C++字符串表达式求值

[复制链接]

已领礼包: 13个

财富等级: 恭喜发财

发表于 2016-12-12 21:52:18 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
关于字符串表达式求值,应该是程序猿们机试或者面试时候常见问题之一,昨天参加国内某IT的机试,压轴便为此题,今天抽空对其进行了研究。

算术表达式中最常见的表示法形式有 中缀、前缀和 后缀表示法。中缀表示法是书写表达式的常见方式,而前缀和后缀表示法主要用于计算机科学领域。

中缀表示法
中缀表示法是算术表达式的常规表示法。称它为 中缀表示法是因为每个操作符都位于其操作数的中间,这种表示法只适用于操作符恰好对应两个操作数的时候(在操作符是二元操作符如加、减、乘、除以及取模的情况下)。对以中缀表示法书写的表达式进行语法分析时,需要用括号和优先规则排除多义性。

Syntax: operand1 operator operand2 Example: (A+B)*C-D/(E+F)

前缀表示法
前缀表示法中,操作符写在操作数的前面。这种表示法经常用于计算机科学,特别是编译器设计方面。为纪念其发明家 ― Jan Lukasiewicz(请参阅参考资料),这种表示法也称 波兰表示法。

Syntax  : operator operand1 operand2 Example : -*+ABC/D+EF

后缀表示法
在后缀表示法中,操作符位于操作数后面。后缀表示法也称 逆波兰表示法(reverse Polish notation,RPN),因其使表达式求值变得轻松,所以被普遍使用。

Syntax  : operand1 operand2 operator Example : AB+C*DEF+/-

字符串表达式求值,一般来说采用如下方式:

一.  中缀表达式到后缀表达式的转换

要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须了解操作符的优先级和结合性。 优先级或者说操作符的强度决定求值顺序;优先级高的操作符比优先级低的操作符先求值。 如果所有操作符优先级一样,那么求值顺序就取决于它们的 结合性。操作符的结合性定义了相同优先级操作符组合的顺序(从右至左或从左至右)。

Left associativity  : A+B+C = (A+B)+C
Right associativity : A^B^C = A^(B^C)
转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:

初始化一个空堆栈,将结果字符串变量置空。
从左到右读入中缀表达式,每次一个字符。
如果字符是操作数,将它添加到结果字符串。
如果字符是个操作符,弹出(pop)操作符,直至遇见开括号(opening parenthesis)、优先级较低的操作符或者同一优先级的右结合符号。把这个操作符压入(push)堆栈。
如果字符是个开括号,把它压入堆栈。
如果字符是个闭括号(closing parenthesis),在遇见开括号前,弹出所有操作符,然后把它们添加到结果字符串。
如果到达输入字符串的末尾,弹出所有操作符并添加到结果字符串。
二. 后缀表达式求值

对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:

初始化一个空堆栈
从左到右读入后缀表达式
如果字符是一个操作数,把它压入堆栈。
如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。
好了,基本思路讨论完毕,我们开始动手写代码,此段代码假设表达式中的预算符只包括四大基本运算符+、-、*、/,为了简化代码,我们也假设表达式中的数字只包括1-9。

函数getPostfixExp用来将一个中缀表达式转换为后缀表达式(也就是逆波兰式).

string getPostfixExp(string& infix)
{
    stack<char> operator_stack;
    string postfix;
    for (auto p : infix)
    {
        if (isOperator(p))
        {
            while (!operator_stack.empty() && isOperator(operator_stack.top()) && priority(operator_stack.top()) >= priority(p))
            {
                postfix.push_back(operator_stack.top());
                postfix.push_back(' ');
                operator_stack.pop();
            }
            operator_stack.push(p);
        }
        else if (p == '(')
        {
            operator_stack.push(p);
        }
        else if (p == ')')
        {
            while (operator_stack.top() != '(')
            {
                postfix.push_back(operator_stack.top());
                postfix.push_back(' ');
                operator_stack.pop();
            }
            operator_stack.pop();
        }
        else
        {
            postfix.push_back(p);
            postfix.push_back(' ');
        }
    }
    while (!operator_stack.empty())
    {
        postfix.push_back(operator_stack.top());
        postfix.push_back(' ');
        operator_stack.pop();
    }
    postfix.pop_back();
    return postfix;
}
[/pre[

其中isOperator函数如下:

复制代码
[pre]
bool isOperator(char ch)
{
    switch(ch)
    {
    case'+':
    case'-':
    case'*':
    case'/':
        return true;
    default:
        return false;
    }
}


其中的priority函数如下:

int priority(char a) {
    int temp;
    if (a == '*' || a == '/')
        temp = 2;
    else if (a == '+' || a == '-')
        temp = 1;
    return temp;
}


得到了后缀表达式,开始我们的求值之旅吧!

int postfixCalculate(string& postfix)
{
    int first, second;
    stack<int> num_stack;
    for (auto p : postfix)
    {
        switch (p)
        {
        //if the item is an operator (+, -, *, or /) then

        //      pop two numbers off the stack

        //      make a calculation:  the second number 
        //            popped-operator-first number

        //      push the result on the stack
        case '*':
            getTwoNums(num_stack, first, second);
            num_stack.push(first * second);
            break;
        case '/':
            getTwoNums(num_stack, first, second);
            num_stack.push(first / second);
            break;
        case '+':
            getTwoNums(num_stack, first, second);
            num_stack.push(first + second);
            break;
        case '-':
            getTwoNums(num_stack, first, second);
            num_stack.push(first - second);
            break;
        case ' ':
            break;
        //   if the item is a number push it on the stack
        default:
            num_stack.push(p - '0');
            break;
        }
    }
    int result = num_stack.top();
    num_stack.pop();
    return result;
}

其中getTwoNums函数如下:

void getTwoNums(stack<int>& num_stack, int& first, int& second)
{
    second = num_stack.top();
    num_stack.pop();

    first = num_stack.top();
    num_stack.pop();
}

好了,全部的代码结束了,写个main函数试试吧!

int main()
{
    string infix;
    cin >> infix;
    string postfix = getPostfixExp(infix);
    cout << postfix << endl;
    cout << postfixCalculate(postfix) << endl;
    system("PAUSE");
    return 0;
}

写在最后的话:

字符串表达式求值方法很多,本文中利用stack结合优先级的方式,解决了这个问题。其他的方法,有表达式树的方式,编译原理的书上有讲解,大家可以结合原理,自己动手实现生成表达式树的代码,然后求值就变得so easy了,当然也有与上面两者迥然不同的方式,大家举一反三,多研究!

论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 6471个

财富等级: 富甲天下

发表于 2016-12-13 00:10:39 | 显示全部楼层
谢谢提供,学习一下!!!!!
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-4-19 15:50 , Processed in 0.389247 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表