我个人觉得你这个题目有点意思,所以虽然怀疑你问题的动机,我还是替你做掉了这个题目。
代码我只考虑一个字符的运算符,包括加减乘除和括号,表达式是否合法我没有做严格检查,假设用户输入的表达式在语法上是合法的。这个程序的输出类似如下,如果直接按回车即可退出程序。
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; }
确实可以当成很好的算法题。
我以前的项目要求是根据现有的一些基础表达式,然后由用户自行配置生成新的表达式,如:
基础表达式1、基础表达式2、基础表达式3,用户可能要配置成:
复合表达式1 = (基础表达式1 + 基础表达式2) / 基础表达式3;
复合表达式2 = (基础表达式1 - 基础表达式2) / 基础表达式3;
复合表达式3 = 复合表达式1 - 复合表达式2;
....
上面只是示例,
并不是简单的根据运算符的优先级加括号。
谢谢。
字符串拼接:
当然,你要
定义一个运算符类,有个属性表示运算符的优先级别。
定义一个表达式类,包含运算符对象的有序集合以及运算符对应的数值的有序集合,然后再在此类里面实现表达式拼接。
在JavaScript中,Eval可以计算出字符表达式的值。.NET中有Eval类的,可以使用指定的脚本引擎计算表达式的值。C++就不知道了。
实现思路大体上就是这样的,因为我以前做过的,源码暂时找不到了。
Eval可以计算出字符表达式的值?
我要的不是表达式的结果,只需要加上括号。求代码。
@sagapo:
C#的代码,而且暂时不在身边
@sagapo: 你这个问题貌似课程作业,我觉得不该解答。
@hoodlum1980:
是作业就不用问你了
学习了!!!
我靠!这烂代码也敢拿出来得瑟
还知道天下有羞耻二字吗
输入:-1-1
我偶尔浏览博文,觉得此题我有点兴趣去做,顺便做了。我考你此题,谅你个不学无术只会招摇撞骗的骗子不会。我给楼主的代码是个demo。看你的回复是拿我的代码去运行去了,我想我的代码够你研究和学习的了。
@hoodlum1980:
靠!错了就错了,竟然拿demo当遮羞布,太无耻了吧
你给出代码时声明这是demo了吗
@garbageMan: 你这个人我就懒得理你。你输入-1-1,第一个被当作运算符在栈里,而操作数那里不够两个,所以在Pop操作数的时候无法pop两个,所以只是形成死循环罢了。我给的代码是demo的意思就是这是解题而非现实应用,所以给的是形式上比较简洁但能说明问题的代码。我适当做了一些我认为可以让结果更好的工作,不然代码可以更简短,但是产生的结果会有更多的括号。我已经说了我假设表达式是合法的,你可以测试很多我的程序未考虑的边界条件,随你。反正你到现在还不懂算法,哎,不乐观的说,你这辈子也就只能评论20行main代码了,你的成就仅限于此,我说你写的文章都是文字垃圾绝对不是故意要攻击才这么说,我是客观实事求是的说的,也许你很难接受这个残酷的事实。
您老人家天天批谭浩强批的乐此不疲,请你来做这道题吧。你可以学习我的代码啊,我的说这种题目早期我也是看过清华的c语言高级编程的那本书的。在bccn论坛里我也给出过tc2.0的图形程序,用于演示求解表达式时的栈图形化的动态示意图的代码的。