二叉树的遍历
2015-11-28 00:39
190 查看
一、先定义二叉树的数据结构
二、基于DFS的递归遍历
1. 先序遍历(PreOrder)
2. 中序遍历(InOrder)
3. 后序遍历(PostOrder)
Summarize: 这三种遍历过程,经过结点的路线是一样的,只是访问各结点的时机不同。
三、利用堆栈实现非递归遍历
借助堆栈实现实际上就是模拟递归函数调用时的入栈和出栈过程,先序遍历是结点第一次入栈即访问,然后分别对该结点的左右子树遍历;中序遍历是当结点出栈时,表明刚完成对该结点左子树的访问,这时访问该结点。
1. 先序非递归
1) 遇到一个结点,访问后入栈,并遍历其左子树;
2) 当左子树遍历完后,从栈顶弹出一个结点;
3) 按该结点的右指针遍历其右子树;
4) 当p为NULL且栈空时,整个遍历过程结束。
2. 中序非递归
同先序基本一样,不同之处在于访问结点的时机,是在结点出栈后访问。
3. 后序非递归
与前两个非递归的过程不同,后序遍历必须在左右子树均访问后才访问根结点,关键是要确定什么时候右子树遍历结束。一种思路是在某一结点的左子树遍历完后将其再次入栈,等到再次出栈就说明其右子树已经遍历结束,这时候就可以访问该结点了。也就是说,在整个后序非递归过程中,每个结点出入栈两次,第一次出栈说明其左子树遍历完成,第二次出栈说明其右子树也遍历完成,而为了区分是第几次出栈,每个结点需要添加一个变量标记。
四、基于BFS的层序遍历
1. 层序遍历用队列实现,从根节点开始,首先将根节点入队,然后执行循环:结点出队并访问,左右结点入队,直到队列为空,形成按广度优先搜索的遍历方式。
基本过程:
1) 根节点入队;
2) 从队列中出队一个结点;
3) 访问该结点;
4) 如果该结点左右子结点非空,依次入队;
5) 队列为空,遍历结束。
2. 有意思的是,基于层序遍历的思想也可以实现先序、中序和后序三种遍历。只要将队列改为栈,同时改变结点的的入栈顺序即可。
1) 先序:入栈顺序为右结点再左结点。
2) 中序:为了知道左右子树什么时候遍历结束,结点需要出入栈两次,第一次出栈后在左右结点间再次入栈,第二次出栈时即可访问该结点。
入栈顺序:右结点->当前结点->左结点
3) 后序:与中序类似,结点第一次出栈后在左右结点之前再次入栈,第二次出栈后访问。入栈顺序:当前结点->右结点->左结点
typedef struct TreeNode *BinTree; struct TreeNode { ElementType data; BinTree left; BinTree right; };
二、基于DFS的递归遍历
1. 先序遍历(PreOrder)
void PreOrder(BinTree T) { if(!T) return; Visit(T); PreOrder(T->left); PreOrder(T->right); }
2. 中序遍历(InOrder)
void InOrder(BinTree T) { if(!T) return; InOrder(T->left); Visit(T); InOrder(T->right); }
3. 后序遍历(PostOrder)
void PostOrder(BinTree T) { if(!T) return; InOrder(T->left); InOrder(T->right); Visit(T); }
Summarize: 这三种遍历过程,经过结点的路线是一样的,只是访问各结点的时机不同。
三、利用堆栈实现非递归遍历
借助堆栈实现实际上就是模拟递归函数调用时的入栈和出栈过程,先序遍历是结点第一次入栈即访问,然后分别对该结点的左右子树遍历;中序遍历是当结点出栈时,表明刚完成对该结点左子树的访问,这时访问该结点。
1. 先序非递归
1) 遇到一个结点,访问后入栈,并遍历其左子树;
2) 当左子树遍历完后,从栈顶弹出一个结点;
3) 按该结点的右指针遍历其右子树;
4) 当p为NULL且栈空时,整个遍历过程结束。
void PreOrder(BinTree T) { stack<BinTree> S; BinTree p = T; while(p || !S.empty()) { while(p){ Visit(p); S.push(p); p = p->left; } if(!S.empty()){ p = S.top(); S.pop(); p = p->right; } } }
2. 中序非递归
同先序基本一样,不同之处在于访问结点的时机,是在结点出栈后访问。
void InOrder(BinTree T) { stack<BinTree> S; BinTree p = T; while(p || !S.empty()) { while(p){ S.push(p); p = p->left; } if(!S.empty()){ p = S.top(); Visit(p); S.pop(); p = p->right; } } }
3. 后序非递归
与前两个非递归的过程不同,后序遍历必须在左右子树均访问后才访问根结点,关键是要确定什么时候右子树遍历结束。一种思路是在某一结点的左子树遍历完后将其再次入栈,等到再次出栈就说明其右子树已经遍历结束,这时候就可以访问该结点了。也就是说,在整个后序非递归过程中,每个结点出入栈两次,第一次出栈说明其左子树遍历完成,第二次出栈说明其右子树也遍历完成,而为了区分是第几次出栈,每个结点需要添加一个变量标记。
void postOrder(BinTree T) { if(!T) return; stack<BinTree> S; BinTree p = T; while(p || !S.empty()) { if(p){ S.push(p); p = p->left; } else{ p = S.top(); S.pop(); p->flag++; if(p->flag == 2){ Visit(p); p = NULL; } else{ S.push(p); p = p->right; } } } }
四、基于BFS的层序遍历
1. 层序遍历用队列实现,从根节点开始,首先将根节点入队,然后执行循环:结点出队并访问,左右结点入队,直到队列为空,形成按广度优先搜索的遍历方式。
基本过程:
1) 根节点入队;
2) 从队列中出队一个结点;
3) 访问该结点;
4) 如果该结点左右子结点非空,依次入队;
5) 队列为空,遍历结束。
void LevelOrder(BinTree T) { if(!T) return; queue<BinTree> Q; BinTree p = T; Q.push(p); while(!Q.empty()) { p = Q.front(); Visit(p); Q.pop(); if(p->left) Q.push(p->left); if(p->right) Q.push(p->right); } }
2. 有意思的是,基于层序遍历的思想也可以实现先序、中序和后序三种遍历。只要将队列改为栈,同时改变结点的的入栈顺序即可。
1) 先序:入栈顺序为右结点再左结点。
void LevelPreOrder(BinTree T) { if(!T) return; stack<BinTree> S; BinTree p = T; S.push(p); while(!S.empty()) { p = S.top(); Visit(p); S.pop(); if(p->right) S.push(p->right); if(p->left) S.push(p->left); } }
2) 中序:为了知道左右子树什么时候遍历结束,结点需要出入栈两次,第一次出栈后在左右结点间再次入栈,第二次出栈时即可访问该结点。
入栈顺序:右结点->当前结点->左结点
void LevelInOrder(BinTree T) { if(!T) return; stack<BinTree> S; BinTree p = T; S.push(p); while(!S.empty()) { p = S.top(); p->flag++; S.pop(); if(p->flag == 2){ Visit(p); } else{ if(p->right) S.push(p->right); S.push(p); if(p->left) S.push(p->left); } } }
3) 后序:与中序类似,结点第一次出栈后在左右结点之前再次入栈,第二次出栈后访问。入栈顺序:当前结点->右结点->左结点
void LevelPostOrder(BinTree T) { if(!T) return; stack<BinTree> S; BinTree p = T; S.push(p); while(!S.empty()) { p = S.top(); p->flag++; S.pop(); if(p->flag == 2){ Visit(p); } else{ S.push(p); if(p->right) S.push(p->right); if(p->left) S.push(p->left); } } }
相关文章推荐
- AVL树-自平衡二叉查找树(Java实现)
- 文件遍历排序函数
- C#数据结构之顺序表(SeqList)实例详解
- Lua 学习笔记之C API 遍历 Table实现代码
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#遍历文件夹后上传文件夹中所有文件错误案例分析
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#中遍历Hashtable的4种方法
- C#数据结构之单链表(LinkList)实例详解
- Erlang中遍历取出某个位置的最大值代码
- 数据结构之Treap详解
- C++实现图的邻接矩阵存储和广度、深度优先遍历实例分析
- C++实现图的邻接表存储和广度优先遍历实例分析
- C语言二叉树的非递归遍历实例分析
- 使用C语言构建基本的二叉树数据结构
- C++非递归队列实现二叉树的广度优先遍历
- php遍历目录方法小结
- 【数据结构与算法】数组应用4:多项式计算Java版