二叉树的中序遍历
2015-09-22 21:37
239 查看
二叉树的遍历(以中序遍历为例)
1.中序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则
( 1 ) 中序遍历左子树
( 2 ) 访问根结点
( 3 ) 中序遍历右子树
我们很容易写出它的递归算法:
void InOrderTraverse(BiNode *pRoot)
{
if(pRoot!=NULL)
{
InOrderTraverse(pRoot->lchild);//中序遍历左子树
visited(pRoot->data);//访问根结点
InOrderTraverse(pRoot->rchild);//
中序遍历右子树
}
}
对于这棵二叉树的中序遍历为:B
D A E C
我们现有来看看它的非递归算法:
思路:我们遍历二叉树时,要依靠中序遍历的规则:注意在(1)和(3)中是遍历而不是访问,这也就隐含了我们是把左子树看成一个集合的而不是一个结点。那么哪一个结点才是第一个被访问的呢?我们用p来寻找。
当我们用p1指向A时想要访问它,可是发现它有左子树,所以我们要先访问它的左子树于是用p指向了它的左子树。当我们访问p所指的结点时必须就要看看它是否还有左子树要访问,如果它的左子树为空,那么我们就可以访问它了。在我们访问了这个结点之后,我们用同样的方式访问了这个结点的右子树(我们用p指向它的右子树),那么问题就来了,我们该如何回来呢?我们如何访问第一个被访问结点的祖先结点呢?
答案是我们必须把祖先结点一一保存下来。我们还会发现先保存的结点后使用,这符合了栈这个数据结构的特性。因此在遍历的过程中,我们用栈来保存祖先结点。
请看图
2.在二叉树的遍历过程中,我们需要做的一件事是要保存访问结点的祖先结点。但是我们还有一个方法可以不用存储祖先结点。那就是可以利用叶子结点的右子树指针来指向这个祖先结点,(你可以发现当我们想要祖先结点的时候都发生在访问了叶子结点之后)。
问题: 不使用递归和栈来中序遍历二叉树。
我们用叶子结点的空指针记录当前结点的位置,然后一旦遍历到了叶子结点发现叶子结点的右指针不为空(指向的是当前结点),那么就认为当前结点的左子树已经遍历完成,利用了线索二叉树的思想
如果当前结点为空,程序退出。
如果当前结点不空
如果当前结点的左儿子为空,那么输出当前结点,当前结点置为当前结点的右儿子
如果当前结点的左儿子不空,找到当前结点的左子树的最右叶子结点。
若最右的叶子结点的右儿子指针为空,则让其指向当前结点,当前结点置为当前结点的左儿子
若最右的叶子结点的右儿子指针不空(指向当前结点),说明当前结点的左子树已经遍历完成,将右儿子指针置为空并将当前结点的置为当当前结点的右子树
1.中序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则
( 1 ) 中序遍历左子树
( 2 ) 访问根结点
( 3 ) 中序遍历右子树
我们很容易写出它的递归算法:
void InOrderTraverse(BiNode *pRoot)
{
if(pRoot!=NULL)
{
InOrderTraverse(pRoot->lchild);//中序遍历左子树
visited(pRoot->data);//访问根结点
InOrderTraverse(pRoot->rchild);//
中序遍历右子树
}
}
对于这棵二叉树的中序遍历为:B
D A E C
我们现有来看看它的非递归算法:
思路:我们遍历二叉树时,要依靠中序遍历的规则:注意在(1)和(3)中是遍历而不是访问,这也就隐含了我们是把左子树看成一个集合的而不是一个结点。那么哪一个结点才是第一个被访问的呢?我们用p来寻找。
当我们用p1指向A时想要访问它,可是发现它有左子树,所以我们要先访问它的左子树于是用p指向了它的左子树。当我们访问p所指的结点时必须就要看看它是否还有左子树要访问,如果它的左子树为空,那么我们就可以访问它了。在我们访问了这个结点之后,我们用同样的方式访问了这个结点的右子树(我们用p指向它的右子树),那么问题就来了,我们该如何回来呢?我们如何访问第一个被访问结点的祖先结点呢?
答案是我们必须把祖先结点一一保存下来。我们还会发现先保存的结点后使用,这符合了栈这个数据结构的特性。因此在遍历的过程中,我们用栈来保存祖先结点。
请看图
下面是算法: while(栈不空或p不空) { while(p不空) { 入栈,向左 } if(栈不空) { 出栈; 访问; 向右; } } | 实现 void InOrderTraverse(BiNode *pRoot) { if(pRoot==NULL) return ; stack<BiNode *> s; while(s.empty()||p!=NULL) { while(p) { s.push(p); p=p->lchild; } if(!s.empty()) { p=s.top(); s.pop(); visit(p)->data; p=p->rchild; } } } |
问题: 不使用递归和栈来中序遍历二叉树。
我们用叶子结点的空指针记录当前结点的位置,然后一旦遍历到了叶子结点发现叶子结点的右指针不为空(指向的是当前结点),那么就认为当前结点的左子树已经遍历完成,利用了线索二叉树的思想
如果当前结点为空,程序退出。
如果当前结点不空
如果当前结点的左儿子为空,那么输出当前结点,当前结点置为当前结点的右儿子
如果当前结点的左儿子不空,找到当前结点的左子树的最右叶子结点。
若最右的叶子结点的右儿子指针为空,则让其指向当前结点,当前结点置为当前结点的左儿子
若最右的叶子结点的右儿子指针不空(指向当前结点),说明当前结点的左子树已经遍历完成,将右儿子指针置为空并将当前结点的置为当当前结点的右子树
3. void MorrisTraversal(BiNode *pRoot) 4. { 5. BiNode *current; 6. BiNode *pre; 7. if(pRoot==NULL) return ; 8. current=pRoot; 9. while(current!=NULL)//树不为空,判断这个结点的左子树是否为空 10. { 11. 12. 13. if(current->lchild==NULL)//左子树为空。 14. { 15. visit(current->data); 16. current=current->rchild; 17. } 18. else//左子树不为空就要找左子树的最右的那个结点 19. { 20. pre=current->lchild; 21. while(pre->rchild!=NULL||pre->rchild!=current) 22. { 23. pre=pre->rchild; 24. } 25. if(pre->rchild==current)//说明已经遍历完了pre的左子树 26. { 27. pre->rchild=NULL; 28. visit(current->data); 29. current=current->rchild; 30. } 31. else 32. { 33. pre->rchild=current; 34. current=current->lchild; 35. } 36. 37. } 38. 39. } 40. 41. } |
相关文章推荐
- main 函数自身递归调用+感想
- maven 学习总结(六)——Maven与Eclipse整合
- 面试题5:从尾到头打印链表
- 个人项目——四则运算题目的随机生成
- Codevs 3027 线段覆盖2
- 安卓:handler下载网络图片(附带有进度条和网络连接判断的APP)
- UVA 136 Ugly Numbers
- jetty JSP support not configured
- 解决Android Studio打开别人包时出现Failed to find: com.android.support:appcompat-v7:21.
- android 事件总线 -- Otto(二) Produce、Subscribe
- 安卓环境搭建
- Linux输入子系统
- (4.1.23.2)Android 属性动画(Property Animation) 完全解析 (下)
- 深度学习Matlab工具箱代码注释——cnnsetup.m
- mongodb高可用集群01---单实例、主从模式、一主多从模式
- uva11300(抽象,推导)
- 经典面试问题
- DOS命令比较两个文本文件txt的内容差异
- Get同步请求数据
- (4.1.23.1)Android 属性动画(Property Animation) 完全解析 (上)