文兄的算法题——算术表达式递归构造二叉树

正在奋斗天津大学的文兄前两天在做算法的时候遇到了一道棘手的题目。
当然,以文兄的聪明才智都不好解决的,我看着也一脸懵逼。然而有万能的度娘,最后还是找到了解决方案。然而说的不是很详细,这里给出我和文兄对这道题的一点理解。


原题如下

这里写图片描述

按照我的思路,应该先把算术表达式转换成后缀表达式,
那么(a+b)乘(c+d) 转换成后缀表达式为 ab + cd + 乘(星号markdown出不来)
用栈存储运算符,用队列存储数—–然后看过HTDP
都知道,Lisp就是这么编译计算的,按照计算机顺序计算的特点很简单就生成二叉树。
然后,这明显不符合题意,其中构造二叉树子函数必须是递归函数
而且,这明显是道算法题,和这些应该没什么关系。

递归构造的方法

我们的想法是既然括号里面的先算,那么最后的运算符肯定不在括号里面。
然后找括号外的符号,因为乘除是要先算的,那么最后的符号如果有加减的话,那就一定是加减。然后最后的符号作为根节点。
但是,我们都不知道在哪划分左子树和右子树,以及怎样寻找最后的
经过不懈的努力(努力搜),终于找到了递归的方法。
算法思想是一致的,对于表达式,找到最后一个被使用的运算符作为划分,以此运算符为界,递归计算左边的值,递归计算右边的值,然后以此运算符进行运算,即可得到结果.
对于算术表达式,肯定先算括号里的, 然后算乘除法,最后才算加减法 .
所以,先考虑加减法法,再考虑乘除法. 括号外的算术运算符可能很多,所以确定一个原则,找整个算式中最右边算术符号.

代码

#include <iostream>  
#define MAXSIZE 1000  

using namespace std;  

//二叉树结点结构体定义  
typedef struct BtreeNode{  
     char data;  
     struct BtreeNode *lchild;  
     struct BtreeNode *rchild;  
}BtreeNode;  

//算术表达式转化二叉树  
/* 
   afa为指向表达式字符串的指针 
   s为要转化的表达式字符串的起始位置 
   e为要转化的表达式字符串的结束位置的后一个 
*/  
BtreeNode* afaToBtree(char *afa,int s,int e){  
    //如果只有一个数那就是叶子结点了  
    if(e-s==1)  
    {  
         BtreeNode* bn=(struct BtreeNode*)malloc(sizeof(struct BtreeNode));  
         bn->data=afa[s];  
         bn->lchild=NULL;  
         bn->rchild=NULL;  
         return bn;  
    }  
    /* 
       local_r记录当前要转化的表达式生成二叉树的根节点操作符的位置 
       flag记录是否当前搜索在括号里面 
       m_m_p记录当前表达式中括号外面最右边的+、-位置 
       a_s_p记录当前表达式中括号外面最右边的*、/位置 
    */  
     int local_r=0,flag=0;   
     int m_m_p=0,a_s_p=0;  
     for(int i=s;i<e;i++)  
     {  
        if(afa[i]=='(')flag++;  
        else if(afa[i]==')')flag--;  
        if(flag==0){  
            if(afa[i]=='*'||afa[i]=='/')  
                m_m_p=i;  
            else if(afa[i]=='+'||afa[i]=='-')  
                a_s_p=i;  
        }  
     }  
     if((m_m_p==0)&&(a_s_p==0))  
         //如果式子整个有括号如(a-b*c+d),即括号外面没有操作符,则去掉括号找二叉树  
         afaToBtree(afa,s+1,e-1);  
     else   
     {  
        //如果有+或者-,则根节点为最右边的+或-,否则是最右边的*或/  
        if(a_s_p>0)local_r=a_s_p;  
        else if(m_m_p>0)local_r=m_m_p;  
        //确定根节点和根节点的左孩子和右孩子  
        BtreeNode* b=(struct BtreeNode*)malloc(sizeof(struct BtreeNode));;  
        b->data=afa[local_r];  
        b->lchild=afaToBtree(afa,s,local_r);  
        b->rchild=afaToBtree(afa,local_r+1,e);  
        return b;  
     }  
}  

void main(){  

    char input[MAXSIZE];  
    int len=0;  
    //初始化  
    memset(input,'\0',sizeof(input));  

    cin >> input ;  

    BtreeNode* myBtree=(struct BtreeNode*)malloc(sizeof(struct BtreeNode));  

    //myBtree就是input算术表达式产生的二叉树的根节点(指针类型)  

    myBtree=afaToBtree(input,0,strlen(input)); 
}

由于要找的是括号外的算术运算符, 所以得想办法避免记录括号内的运算符,所以设置了一个标志,初始为零,一旦遇到一个左括号, +1, 这时候说明目前在括号内,不应该记录运算符, 当遇到右括号,-1,标志恢复为0,这时候说明目前已经走出括号,可以记录运算符 .
扫描完整个表达式的时候,m_m_p记录了最右边的括号外的+-号,a_s_p记录了最右边的括号外的*/号.
把(m_m_p==0)&&(a_s_p==0),说明整个算式被括号包围起来了.
括号外面没有操作符,所以可以递归运算时忽略这对括号。

最后,祝文兄考个好成绩!

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值