文章目录
问题描述 :
内容:(1)请参照链表的ADT模板,设计二叉树并逐步完善的抽象数据类型。(由于该环境目前仅支持单文件的编译,故将所有内容都集中在一个源文件内。在实际的设计中,推荐将抽象类及对应的派生类分别放在单独的头文件中。参考教材、课件,以及网盘中的链表ADT原型文件,自行设计二叉树的ADT。)
注意:二叉树ADT的基本操作的算法设计很多要用到递归的程序设计方法。
(2)ADT的简单应用:使用该ADT设计并实现若干应用二叉树的算法设计。
应用:要求设计一个算法,将中缀表达式转换成表达式二叉树。二叉树的存储结构的建立参见二叉树应用1。
注意:假定输入的中缀表达式为合法的表达式。仅考虑有小括弧的场合。运算符包括+、-、*、/,运算数为整数(不局限于个位数)。另外,为了保证和测试数据保持一致,子表达式的分割请按照“从右至左”的顺序进行。
提示:使用2个栈:一个是保存运算符的栈Optr,一个是保存指向二叉树树根的指针栈Opnd。算法函数返回指向表达式二叉树root的指针。
参考函数原型:
//将中缀表达式转换成表达式二叉树 (用户函数)
BinaryTreeNode<string> * Inffix_BianryTree(string &inffix);
辅助函数:
//操作数位数处理函数 (用户函数)
int getData(string &s, int &n){
int data = 0;
while( s[n] >= '0' && s[n] <= '9'){
data = data * 10 + s[n] - '0';
n++;
}
return data;
}
输入说明 :
第一行:中缀表达式字符串
输出说明 :
第一行:表达式二叉树前序遍历结果(前缀式)
第二行:表达式二叉树中序遍历结果(中缀式,未带加括号处理)
第三行:表达式二叉树后序遍历结果(后缀式)
输入范例 :
1+(12+13-400/(10*(30/(10+5))))*62
输出范例 :
+ 1 * + 12 - 13 / 400 * 10 / 30 + 10 5 62
1 + 12 + 13 - 400 / 10 * 30 / 10 + 5 * 62
1 12 13 400 10 30 10 5 + / * / - + 62 * +
思路分析
- 我是个懒人,第一个思路居然是将其转成中缀表达式,或者后缀表达式或者前缀表达式,直接然后再遍历,进行生成树。多简单不是吗?
回归正题
- 既然是将正常的表达式变成对应的中缀表达式,经过树,只是多了一道工序,所以,可以适当的参考一下不经过二叉树,直接将表达式生成中缀表达式的程序传送门
- 在此基础上,将计算改成生成子树,并将子树的根放到对应的栈中。
- 再来看一遍,栈变化的过程,进而得出如何改造栈的措施
- 修改为
- 将OPREND数字栈转为保存二叉树的结点的指针栈
- 将运算过程,变成生成子树的过程
生成的图片和老师的不一样,区别如下
- 生成的结果和老师有点不一样的,区别如下
- 主要是同级之间,并没有如:加和减并没有优先级之分,所以修改一下比较优先级的函数即可。
- 生成的效果和老师的是一致的。
实现伪码——此处参考老师,自己的代码一开始就想错了
int getData(string &s,int &n){
int data = 0;
while(s[n]>= '0' && s[n] <= '9'){
data = data * 10+s[n] - '0';
n ++;
}
return data;
}
BinaryTreeNode<string> * Inffix_BianryTree(string &inffix) {
SqStack <BinaryTreeNode<string> *> Opnd;
SqStack<char> Optr;
BinaryTreeNode<string> *p, *left, *right;
char topOp=' ', tmp;
string str = "s",temp = "";
int flag = 0;
//顺序扫描中缀表达式
for(int i = 0 ; i < inffix.size(); ++i) {
//跳过中缀表达式中的空格
if(inffix[i] == ' ') continue;
//根据扫描到的字符串进行不同的处理
switch(inffix[i]) {
case ')':
//开闭括号之间表达式转换处理
//符号栈为空,直接入栈
if(!Optr.StackisEmpty()) Optr.pop(topOp);
//符号栈不为空,就开始遍历直到找到左括号位置
while(!Optr.StackisEmpty() && topOp != '(') {
//遍历的过程中,生成子树
str[0] = topOp;
Opnd.pop(right);
Opnd.pop(left);
p = new BinaryTreeNode<string>(str, left, right);
Opnd.push(p);
Optr.pop(topOp);
}
//括号的等级减少
flag --;
//如果栈顶不是做左括号,说明括号不完整,直接退出
if(topOp != '(') {
return NULL;
}
break;
case '(':
//处理左括号的,情况
//没遇到一个左括号,需要处理的优先级就增加1
flag ++;
tmp = '(';
Optr.push(tmp);
break;
//乘除是等价的符号
case '*':
case '/':
//如果符号栈为空,直接入栈
if(!Optr.StackisEmpty()) Optr.GetTop(topOp);
//不存在括号的嵌套运算,并且栈顶是同类符号
while(!Optr.StackisEmpty() && flag == 0 && (topOp == '*' || topOp == '/')) {
//基本操作:栈内的符号生成结点,并入节点栈
Optr.pop(topOp);
str[0] = topOp;
Opnd.pop(right);
Opnd.pop(left);
p = new BinaryTreeNode<string>(str, left, right);
Opnd.push(p);
}
//将待入栈符号入栈
Optr.push(inffix[i]);
break;
case '+':
case '-':
//针对加减号进行处理,等级是相同的,前提是等级为没有括号
if(!Optr.StackisEmpty()) Optr.GetTop(topOp);
while(!Optr.StackisEmpty() && flag == 0) {
Optr.pop(topOp);
str[0] = topOp;
Opnd.pop(right);
Opnd.pop(left);
p = new BinaryTreeNode<string>(str, left, right);
Opnd.push(p);
}
Optr.push(inffix[i]);
break;
default:
//如果存在括号
while(inffix[i] >= '0' && inffix[i] <= '9' ){
temp.push_back(inffix[i]);
i ++;
}
p = new BinaryTreeNode<string>(temp);
Opnd.push(p);
--i;
temp = "";
}
}
//当遍历完整个字符串时
if(!Optr.StackisEmpty()) {
Optr.GetTop(topOp);
while(!Optr.StackisEmpty() ) {
Optr.pop(topOp);
str[0] = topOp;
Opnd.pop(right);
Opnd.pop(left);
p = new BinaryTreeNode<string>(str, left, right);
Opnd.push(p);
}
}
return p;
}
事故现场
第一次提交
- 格式不正确,我真的觉得很扯,因为前序遍历后面没有空格,中序和后序遍历又有空格。索性直接看答案吧。
- 只需要把第一个逗号,给消除就行了
- 弄了半天,还真不知道怎么判定是否已经到了的遍历顺序的末尾,根据是否已经到了末尾进行添加
- 直接看样例,居然是多了个换行符,真的是。。。。。
关于错误的样例
- 魔改的终究有问题,上面的似乎涉及到了优先级的问题
- 如果改为对应优先级有判定的符号,似乎就可以通过
- 并不能找到具体的原因,只能参考一下老师的代码了
- 主要的矛盾还是在括号上,针对括号内部的采用的从右向左靠拢,针对不在括号内部的,采用从左向右靠拢
- 所以,针对括号要特殊处理。下述代码中,在针对优先级的处理中与我们不同,主要在于对于括号里的,不需要比较,优先级,直接进行入栈。
分析与总结
- 关于这道题有很多的疑惑的,如果按照自己最初的方法去计算,大部分式一致的,但是有一一部分不一致。但是不一致,也仅仅是后缀或者前缀表达式不一致,中缀式是一致的,计算的结果也是一致的。