首页新闻找找看学习计划

写程序按照运算符的优先级给表达式加括号

2
悬赏园豆:50 [已解决问题] 解决于 2012-09-13 12:26

如: 对表达式2+5-3*2加括号为((2+5)-(3*2))

如          a+b*c-(d+e)   加括号变为 ((a+(b*c))-(d+e))
sagapo的主页 sagapo | 初学一级 | 园豆:64
提问于:2012-09-12 00:52
< >
分享
最佳答案
0

我个人觉得你这个题目有点意思,所以虽然怀疑你问题的动机,我还是替你做掉了这个题目。

代码我只考虑一个字符的运算符,包括加减乘除和括号,表达式是否合法我没有做严格检查,假设用户输入的表达式在语法上是合法的。这个程序的输出类似如下,如果直接按回车即可退出程序。

Input: 1+2*3+4*5
After: (1+(2*3))+(4*5)
Input: A1+A2+A3/A4*A5+A6
After: ((A1+A2)+((A3/A4)*A5))+A6
Input: 1+(2+3)*(a+b*c)
After: 1+((2+3)*(a+(b*c)))

为了表达式的整洁,最外层的括号我认为没必要加,如果要加,对代码的注释处做个改动即可。

// AddBracket.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>

//扫描所处的状态
#define STATE_IDLE        0
#define STATE_INNUM        1 //位于某个操作数中间
#define STATE_OPERATOR    2 //遇到运算符

//获取运算符的优先级
int GetPriorLevel(char ch)
{
    switch(ch)
    {
    case ')':
        return 1;
    case '+':
    case '-':
        return 2;
    case '*':
    case '/':
        return 3;
    
    case '(':
        return 100;
    }
    return 1;
}

//向字符串指定位置插入一个字符
void InsertChar(char* pos, char ch)
{
    char* p = pos + strlen(pos);
    while(p >= pos)
    {
        p[1] = p[0];
        --p;
    }
    *pos = ch;
}

//弹出两个操作数进行计算,这里是加括号
//返回值:插入的字符数
int PopTwoNums(char* s1_head[], char* s1_end[], char* s2, int* pTop1, int* pTop2)
{
    //两个栈里有内容吗?
    if(*pTop1 < 1 || *pTop2 < 0)
        return 0;

    //为了简洁,如果已经是"(Num1 + Num2)"的形式,则直接放弃加括号
    //因为"(...)" 变成 "((...))" 毫无意义,且降低表达式可读性
    char* num1 = s1_head[*pTop1 - 1];
    char* num2 = s1_end[*pTop1];
    if(*(num1 - 1) == '(' && *(num2 + 1) == ')')
    {
        //把上面的操作数弹出(保留下方的操作数!相当于num1 oper num2 的计算结果)
        //注意这里不会把操作数向外扩展到"()"边界上,而是在调用方进行外扩
        s1_end[*pTop1 - 1] = s1_end[*pTop1];
        s1_head[*pTop1] = NULL;        //仅为了调试时观察栈的情况
        s1_end[*pTop1] = NULL;        //仅为了调试时观察栈的情况
        --(*pTop1);

        //把栈内的当前操作符弹出
        s2[*pTop2] = 0; //仅为了调试时观察栈的情况
        --(*pTop2);
        return 0;
    }

    InsertChar(num1, '(');
    InsertChar(num2 + 2, ')');
    s1_end[*pTop1] += 2; //尾部被新插入的 "()" 向后推动两个字符距离

    //把上面的操作数弹出(保留下方的操作数!相当于num1 oper num2 的计算结果)
    s1_end[*pTop1 - 1] = s1_end[*pTop1];
    s1_head[*pTop1] = NULL;        //仅为了调试时观察栈的情况
    s1_end[*pTop1] = NULL;        //仅为了调试时观察栈的情况
    --(*pTop1);

    //把栈内的当前操作符弹出
    s2[*pTop2] = 0; //仅为了调试时观察栈的情况
    --(*pTop2);
    return 2;
}

//获取栈内运算符优先级
int GetPriorInStack(char* s2, int top2)
{
    if(top2 >= 0)
        return GetPriorLevel(s2[top2]);
    else
        return 0;
}

//给一个表达式加括号(对该字符串进行就地修改!)
void AddBracket(char* input)
{
    //  FirstNumber+100;
    //  |         |
    // head      end
    //两个栈,s1,s2
    char* s1_head[256] = { 0 };    //操作数起始位置栈,例如"Num1"
    char* s1_end[256] = { 0 }; //操作数结束位置
    char s2[256] = { 0 };    //运算符栈
    int top1 = -1;            //操作符栈的栈顶指针
    int top2 = -1;            //运算符栈的栈顶指针
    int state = STATE_IDLE;

    char* p = input, ch = 0;
    while(*p)
    {
        ch = *p;
        //是运算符吗?
        if(strchr("+-*/()", ch) != NULL)
        {
            //记录操作数的结尾
            if(state == STATE_INNUM)
                s1_end[top1] = p - 1;

            int level_out = GetPriorLevel(ch); //当前运算符的优先级
            int level_in = GetPriorInStack(s2, top2);
            
            //处理掉所有栈内的同等或高优先级运算符

            while(top2 >= 0 && s2[top2] != '(' && level_in >= level_out)
            {
                //栈内运算符优先级高!可以加括号了
                p += PopTwoNums(s1_head, s1_end, s2, &top1, &top2);

                //更新栈内运算符优先级
                level_in = GetPriorInStack(s2, top2);
            }
            
            //把该运算符压入栈中
            ++top2;
            s2[top2] = ch;

            //检测运算符栈的顶部是否是匹配的'()',如果是,把他们弹出即可;
            //并把顶部操作数的 Head 和 End 向两侧扩展一个字符的距离
            if(top2 > 0 && s2[top2] == ')' && s2[top2-1] == '(')
            {
                s2[top2] = 0;
                s2[top2-1] = 0;
                top2 -= 2;

                // (1+...+3)  ->  (1+...+3)
                //  |     |       |       |
                // head  end     head    end
                if(top1 >= 0)
                {
                    s1_head[top1]--;
                    s1_end[top1]++;
                }
            }
            state = STATE_IDLE;
        }
        else if(state == STATE_IDLE)
        {
            //操作数入栈
            ++top1;
            s1_head[top1] = p;
            state = STATE_INNUM; //在数字中间,则不会做任何处理继续向后扫描
        }
        ++p;
    }

    //由于扫描到字符串结尾,所以需要把结束标记设置给s1_end
    if(state == STATE_INNUM)
        s1_end[top1] = p - 1;

    //字符串扫描结束,查看运算符栈里是否有内容?
    //如果有,一定是按照优先级降序排列,所以依次弹出即可!
    //如果写成 top2 >= 0 则会在表达式最外围加一个括号,但这是没必要的
    //例如:... 不必变成 (...)
    //例如 1+(2*3) 即可,不必写成 (1 + (2*3))
    //如果写成 top2 > 0,则不会显示表达式最外围那层大括号
    while(top2 > 0 && top1 >= 1)
    {
        PopTwoNums(s1_head, s1_end, s2, &top1, &top2);
    }
}

int main(int argc, char* argv[])
{
    char input[256] = { 0 };
    while(1)
    {
        printf("Input: ");
        gets(input);

        if(strlen(input) <= 0) break;

        AddBracket(input);
        printf("After: %s\n", input);
    }
    return 0;
}
收获园豆:50
hoodlum1980 | 小虾三级 |园豆:533 | 2012-09-13 08:13

确实可以当成很好的算法题。

我以前的项目要求是根据现有的一些基础表达式,然后由用户自行配置生成新的表达式,如:

基础表达式1、基础表达式2、基础表达式3,用户可能要配置成:

复合表达式1 = (基础表达式1 + 基础表达式2) / 基础表达式3;

复合表达式2 = (基础表达式1 - 基础表达式2) / 基础表达式3;

复合表达式3 = 复合表达式1 - 复合表达式2;

....

上面只是示例,

并不是简单的根据运算符的优先级加括号。

TigerSpringLiu | 园豆:196 (初学一级) | 2012-09-13 09:26

谢谢。

sagapo | 园豆:64 (初学一级) | 2012-09-13 12:24
其他回答(3)
0

字符串拼接:

当然,你要

定义一个运算符类,有个属性表示运算符的优先级别。

定义一个表达式类,包含运算符对象的有序集合以及运算符对应的数值的有序集合,然后再在此类里面实现表达式拼接。

在JavaScript中,Eval可以计算出字符表达式的值。.NET中有Eval类的,可以使用指定的脚本引擎计算表达式的值。C++就不知道了。

实现思路大体上就是这样的,因为我以前做过的,源码暂时找不到了。

TigerSpringLiu | 园豆:196 (初学一级) | 2012-09-12 08:56

Eval可以计算出字符表达式的值?

我要的不是表达式的结果,只需要加上括号。求代码。

支持(0) 反对(0) sagapo | 园豆:64 (初学一级) | 2012-09-12 12:16

@sagapo: 

C#的代码,而且暂时不在身边

支持(0) 反对(0) TigerSpringLiu | 园豆:196 (初学一级) | 2012-09-12 12:19

@sagapo: 你这个问题貌似课程作业,我觉得不该解答。

支持(0) 反对(0) hoodlum1980 | 园豆:533 (小虾三级) | 2012-09-13 04:01

@hoodlum1980:

是作业就不用问你了

支持(0) 反对(0) sagapo | 园豆:64 (初学一级) | 2012-09-13 12:23
0

学习了!!!

jason2013 | 园豆:1998 (小虾三级) | 2012-09-12 09:40
0

我靠!这烂代码也敢拿出来得瑟

还知道天下有羞耻二字吗

输入:-1-1

garbageMan | 园豆:313 (菜鸟二级) | 2012-09-13 10:42

我偶尔浏览博文,觉得此题我有点兴趣去做,顺便做了。我考你此题,谅你个不学无术只会招摇撞骗的骗子不会。我给楼主的代码是个demo。看你的回复是拿我的代码去运行去了,我想我的代码够你研究和学习的了。

支持(0) 反对(0) hoodlum1980 | 园豆:533 (小虾三级) | 2012-09-13 12:00

@hoodlum1980: 

靠!错了就错了,竟然拿demo当遮羞布,太无耻了吧

你给出代码时声明这是demo了吗

支持(0) 反对(1) garbageMan | 园豆:313 (菜鸟二级) | 2012-09-13 12:02

@garbageMan:  你这个人我就懒得理你。你输入-1-1,第一个被当作运算符在栈里,而操作数那里不够两个,所以在Pop操作数的时候无法pop两个,所以只是形成死循环罢了。我给的代码是demo的意思就是这是解题而非现实应用,所以给的是形式上比较简洁但能说明问题的代码。我适当做了一些我认为可以让结果更好的工作,不然代码可以更简短,但是产生的结果会有更多的括号。我已经说了我假设表达式是合法的,你可以测试很多我的程序未考虑的边界条件,随你。反正你到现在还不懂算法,哎,不乐观的说,你这辈子也就只能评论20行main代码了,你的成就仅限于此,我说你写的文章都是文字垃圾绝对不是故意要攻击才这么说,我是客观实事求是的说的,也许你很难接受这个残酷的事实。

支持(0) 反对(0) hoodlum1980 | 园豆:533 (小虾三级) | 2012-09-13 12:14

您老人家天天批谭浩强批的乐此不疲,请你来做这道题吧。你可以学习我的代码啊,我的说这种题目早期我也是看过清华的c语言高级编程的那本书的。在bccn论坛里我也给出过tc2.0的图形程序,用于演示求解表达式时的栈图形化的动态示意图的代码的。

支持(0) 反对(0) hoodlum1980 | 园豆:533 (小虾三级) | 2012-09-13 12:19
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册