中缀式表达式转成二叉树(无论再差,人总归是要往上游的)

83 篇文章 5 订阅
67 篇文章 2 订阅

问题描述 :

内容:(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;
}
事故现场
第一次提交

在这里插入图片描述

  • 格式不正确,我真的觉得很扯,因为前序遍历后面没有空格,中序和后序遍历又有空格。索性直接看答案吧。
    在这里插入图片描述
  • 只需要把第一个逗号,给消除就行了
    在这里插入图片描述
  • 弄了半天,还真不知道怎么判定是否已经到了的遍历顺序的末尾,根据是否已经到了末尾进行添加

在这里插入图片描述

  • 直接看样例,居然是多了个换行符,真的是。。。。。
关于错误的样例

在这里插入图片描述

  • 魔改的终究有问题,上面的似乎涉及到了优先级的问题
  • 如果改为对应优先级有判定的符号,似乎就可以通过
  • 在这里插入图片描述
  • 并不能找到具体的原因,只能参考一下老师的代码了
  • 主要的矛盾还是在括号上,针对括号内部的采用的从右向左靠拢,针对不在括号内部的,采用从左向右靠拢
  • 所以,针对括号要特殊处理。下述代码中,在针对优先级的处理中与我们不同,主要在于对于括号里的,不需要比较,优先级,直接进行入栈。
分析与总结
  • 关于这道题有很多的疑惑的,如果按照自己最初的方法去计算,大部分式一致的,但是有一一部分不一致。但是不一致,也仅仅是后缀或者前缀表达式不一致,中缀式是一致的,计算的结果也是一致的。
虽然我很垃圾,但是我还是要往上游啊!!有没有一块游的,可以留言或者加扣扣651378276
  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
表达式树转换成中缀可以使用中序遍历的方,但是由于表达式树中的操作符可能会存在优先级,因此在遍历的过程中需要注意添加括号。 具体的转换过程如下: 1.如果当前节点是操作符节点,那么需要考虑添加括号的情况。如果当前节点的操作符优先级小于其父节点的操作符优先级,那么需要在当前操作符节点的左右子树上添加括号。如果当前节点是根节点,那么不需要添加括号。 2.如果当前节点是数字节点,那么直接输出其值。 下面是一个示例: 假设表达式二叉树为: ``` * / \ + - / \ / \ 1 2 3 4 ``` 中序遍历的结果为: ``` (1 + 2) * (3 - 4) ``` 具体的转换过程如下: 1.从根节点开始遍历,当前节点是操作符节点,且不是根节点,因此需要考虑添加括号的情况。当前操作符节点是乘号,其优先级大于其父节点减号的优先级,因此不用添加括号。继续遍历左子树。 2.当前节点是操作符节点,且不是根节点,因此需要考虑添加括号的情况。当前操作符节点是加号,其优先级小于其父节点乘号的优先级,因此需要在其左右子树上添加括号。输出左括号,遍历左子树,输出节点值为1,输出加号,遍历右子树,输出节点值为2,输出右括号。 3.当前节点是乘号,其优先级大于加号,不需要添加括号。输出乘号。 4.当前节点是操作符节点,且不是根节点,因此需要考虑添加括号的情况。当前操作符节点是减号,其优先级小于乘号,需要在其左右子树上添加括号。输出左括号,遍历左子树,输出节点值为3,输出减号,遍历右子树,输出节点值为4,输出右括号。 5.遍历结束,输出结果为:(1 + 2) * (3 - 4)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值