【二叉树遍历算法】——前/中/后序递归与非递归的实现
2015-10-17 11:07
429 查看
昨天面试了美团,面试官要求写出二叉树的中序遍历,要求实现递归与非递归方式。在写非递归的时候,一个while循环中忘记控制空指针,╮(╯▽╰)╭当时脑子一片空白不在状态。回来后告诉自己,这类问题以后绝不会发生!下述6种实现方法希望大家都能理解,并快速能手写代码出来。(理解,懂得思路才是王道,因为面试的时候人的情绪相对来说是比较紧张,需要掌握得更熟悉)
先(前)序递归遍历
中序递归遍历
后序递归遍历
先(前)序非递归遍历
中序非递归遍历
后序非递归遍历
下面是我的理解和记住实现的几个技巧:
递归实现中,先(前)/中/后序的visit函数的调用在两句递归调用语句的前/中/后位置。
非递归实现中,先(前)和中序都是通过先把根压进栈,然后一直向左走走到尽头,弹出空指针后向右走一步,循环操作,不同在于:先(前)序遍历打印的时候是在添加左子树之前(因为根要先打印嘛),而中序是在添加右子树之前(因为根要比右子树先打印)。
非递归实现中,打印的条件为当前没有右子树或者右子树已经访问过;需要借助一个结构体来记录右子树是否被访问过;同样需要先把根压栈再向左走到尽头;假设有一棵树(ABC),A为根,B为左子树,C为右子树,那么利用打印条件和压栈顺序,A其实不可能优先于BC打印的,而怎么控制B先于C打印呢?就是在每次不满足打印条件的时候,就拿到其右子树后一直往左下方走,把结点都push进栈就可以。
先(前)序递归遍历
void preorderTraverse(BiTree T){ if(T){ visit(T); preorderTraverse(T->lChild); preorderTraverse(T->rChild); } }
中序递归遍历
void inOrderTraverse(BiTree T){ if(T){ inOrderTreverse(T->lChild); visit(T); inOrderTreverse(T->rChild); } }
后序递归遍历
void lastorderTraverse(BiTree T){ if(T){ preorderTraverse(T->lChild); preorderTraverse(T->rChild); visit(T); } }
先(前)序非递归遍历
void preorderTraverse(BiTree T){ InitStack(S); Push(S,T); while(!StackEmpty(S)){ while(GetTop(S,p) && p){ //先打印再添加左子树 visit(p); Push(S,p->lChild); } //弹出空指针 Pop(S,p); //向右走一步 if(!StackEmpty(S)){ Pop(S,p); Push(S,p->rChild); } } }
中序非递归遍历
void inOrderTraverse(BiTree T){ InitStack(S); Push(S,T); while(!StackEmpty(S)){ //一直向左找,直到最后一个为空指针 while(GetTop(S,p) && p) Push(S,p->lChild); //弹出空指针 Pop(S,p); //向右走一步 if(!StackEmpty(S)){ Pop(S,p); visit(p); Push(S,p->rChild); } } }
后序非递归遍历
typedef struct{ Node *p; //二叉树结点 int isVisited; //1表示所指的有结点被访问过 }SNode 非递归后序 void lastorderTraverse(BiTree T){ InitStack(S); p = T; //一直往左边走 while(p){ Push(S,p,0); p = p ->lchild; //表示把当前根节点push进栈,同时isVisited值为0 } while(!StackEmpty(S)){ GetTop(S,sNode); //如果右子树已经访问过或者没有右子树 if(!sNOde->p->rchild || sNode.isVisited == 1){ Pop(S,p); visit(p); }else{ //此时应该从右子树开始一直往左下方走到尽头 sNode.isVisited = 1; p = sNode->rChild; while(p){ Push(S,p,0); p = p->lChild; } } } }
下面是我的理解和记住实现的几个技巧:
递归实现中,先(前)/中/后序的visit函数的调用在两句递归调用语句的前/中/后位置。
非递归实现中,先(前)和中序都是通过先把根压进栈,然后一直向左走走到尽头,弹出空指针后向右走一步,循环操作,不同在于:先(前)序遍历打印的时候是在添加左子树之前(因为根要先打印嘛),而中序是在添加右子树之前(因为根要比右子树先打印)。
非递归实现中,打印的条件为当前没有右子树或者右子树已经访问过;需要借助一个结构体来记录右子树是否被访问过;同样需要先把根压栈再向左走到尽头;假设有一棵树(ABC),A为根,B为左子树,C为右子树,那么利用打印条件和压栈顺序,A其实不可能优先于BC打印的,而怎么控制B先于C打印呢?就是在每次不满足打印条件的时候,就拿到其右子树后一直往左下方走,把结点都push进栈就可以。
相关文章推荐
- java.util.concurrent 使用详解
- Unix权限管理
- 概率的意义-深度理解
- WAITEVENT: "buffer busy waits" Reference Note (Doc ID 34405.1)
- HTML学习笔记之简单网页属性设置
- 如何桥接路由器让无线信号无死角
- CATransform3D详解
- Android开发面试经——5.常见面试官提问Android题2
- 简单粗暴的so加解密实现
- MFC的CreateTimerQueueTimer和timeSetEvent两个高精度定时器的比较
- ExtJs之格式化(Ext.util.Format)
- SQL DROP撤销索引、表以及数据库 高级教程
- poj1459多源点网络流入门
- 软件需求模式 读书笔记二
- 浅析清除session的几种方法
- 正常利润
- WAITEVENT: "read by other session" Reference Note (Doc ID 732891.1)
- Queue类
- SDUT 数据结构实验之栈六:下一较大值(二)
- 小学奥数 蓄水池水管问题