文兄的算法题——算术表达式递归构造二叉树
2017-12-22 12:06
260 查看
正在奋斗天津大学的文兄前两天在做算法的时候遇到了一道棘手的题目。
当然,以文兄的聪明才智都不好解决的,我看着也一脸懵逼。然而有万能的度娘,最后还是找到了解决方案。然而说的不是很详细,这里给出我和文兄对这道题的一点理解。
按照我的思路,应该先把算术表达式转换成后缀表达式,
那么(a+b)乘(c+d) 转换成后缀表达式为 ab + cd + 乘(星号markdown出不来)
用栈存储运算符,用队列存储数—–然后看过HTDP
都知道,Lisp就是这么编译计算的,按照计算机顺序计算的特点很简单就生成二叉树。
然后,这明显不符合题意,其中构造二叉树子函数必须是递归函数。
而且,这明显是道算法题,和这些应该没什么关系。
然后找括号外的符号,因为乘除是要先算的,那么最后的符号如果有加减的话,那就一定是加减。然后最后的符号作为根节点。
但是,我们都不知道在哪划分左子树和右子树,以及怎样寻找最后的
经过不懈的努力(努力搜),终于找到了递归的方法。
算法思想是一致的,对于表达式,找到最后一个被使用的运算符作为划分,以此运算符为界,递归计算左边的值,递归计算右边的值,然后以此运算符进行运算,即可得到结果.
对于算术表达式,肯定先算括号里的, 然后算乘除法,最后才算加减法 .
所以,先考虑加减法法,再考虑乘除法. 括号外的算术运算符可能很多,所以确定一个原则,找整个算式中最右边算术符号.
由于要找的是括号外的算术运算符, 所以得想办法避免记录括号内的运算符,所以设置了一个标志,初始为零,一旦遇到一个左括号, +1, 这时候说明目前在括号内,不应该记录运算符, 当遇到右括号,-1,标志恢复为0,这时候说明目前已经走出
4000
括号,可以记录运算符 .
扫描完整个表达式的时候,m_m_p记录了最右边的括号外的+-号,a_s_p记录了最右边的括号外的*/号.
把(m_m_p==0)&&(a_s_p==0),说明整个算式被括号包围起来了.
括号外面没有操作符,所以可以递归运算时忽略这对括号。
最后,祝文兄考个好成绩!
当然,以文兄的聪明才智都不好解决的,我看着也一脸懵逼。然而有万能的度娘,最后还是找到了解决方案。然而说的不是很详细,这里给出我和文兄对这道题的一点理解。
原题如下
按照我的思路,应该先把算术表达式转换成后缀表达式,
那么(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,这时候说明目前已经走出
4000
括号,可以记录运算符 .
扫描完整个表达式的时候,m_m_p记录了最右边的括号外的+-号,a_s_p记录了最右边的括号外的*/号.
把(m_m_p==0)&&(a_s_p==0),说明整个算式被括号包围起来了.
括号外面没有操作符,所以可以递归运算时忽略这对括号。
最后,祝文兄考个好成绩!
相关文章推荐
- 二叉树创建及遍历算法(递归及非递归)
- 二叉树创建及遍历算法(递归及非递归)
- 第十一周项目1-二叉树算法验证(2) 二叉树构造算法验证
- 二叉树的前序、中序、后序遍历的算法(递归形式和非递归形式)
- 二叉树链表实现的各种算法(递归)
- 二叉树三种遍历算法的递归和非递归实现(C++)
- 算法导论第十二章习题12.3-1---二叉树插入的递归版本
- 【算法】二叉树非递归遍历
- 二叉树创建及遍历算法(递归及非递归)
- 结点遍历C语言实现二叉树的常用的算法(递归与非递归实现遍历)
- 二叉树创建及遍历算法(递归及非递归)
- 非递归二叉树的算法
- 二叉树创建及遍历算法(递归及非递归)(转)
- 【二叉树】非递归遍历的通用算法:前序、中序和后序
- 第十一周 项目1-(2)二叉树构造的算法认证--由后序和先序构造二叉树
- C语言构造并递归遍历二叉树
- 算法:二叉树的先序遍历、中序遍历、后序遍历(递归及非递归方式)的java代码实现
- 算法:非递归遍历二叉树
- 二叉树创建及遍历算法(递归及非递归)
- 算法习题43:递归和非递归实现二叉树的前序遍历