二叉树的遍历
前言:二叉树的遍历是进行各种各样的操作的框架, 对其框架进行应用可以完成很多任务。树的遍历类型按DFS(深度优先搜索)分为:先序,中序,后序遍历。树的BFS(广度优先搜索)又可称为层次遍历
BFS:先序,中序,后序。
树的遍历类型依据是遍历根结点的顺序,先序遍历先访问根结点,再访问子树;中序遍历先访问左(右)子树再访问右(左)子树;后序遍历先访问子树再访问根结点。
需要注意的是,先序,中序,后序的区别非常小,只是操作的位置不一样,详情可以看一下下面的代码
先序遍历
void traverse(BiTree T){
if(T == NULL) return; //终止条件
//operations...
traverse(T->left);
traverse(T->right);
}
中序遍历
void traverse(BiTree T){
if(T == NULL) return; //终止条件
traverse(T->left);
//operations...
traverse(T->right);
}
后序遍历
void traverse(BiTree T){
if(T == NULL) return; //终止条件
traverse(T->left);
traverse(T->right);
//operations...
}
这三个框架可以完成一些基本的操作如:增删改查。他们的递归效率近似于迭代,写起来也很快,因此很多与二叉树相关的问题可以用递归来解决。
//将每个结点的值+1,中序遍历
void traverse(BiTree T){
if(T == NULL) return;
traverse(T->left);
++ T->val;
traverse(T->right);
}
//求深度,无非是多利用了返回值
int Depth(BiTree T){
if(T == NULL) return 0;
int left = traverse(T->left);
int right = traverse(T->right);
return 1 +((left > right)? left: right);
}
//求叶子结点数
void Leaves(BiTree T, int & cnt){
if(T == NULL) return;
if(NULL == T->left && NULL == T->right) ++cnt;
Leaves(T->left);
Leaves(T->right);
}
...
关于三种遍历的实际情况
我们以这棵二叉树为例
先序遍历:
5→1→0→4→8→7→9
中序遍历:
0→1→4→5 →7 →8 →9
后序遍历:
0 →4 → 1 → 7 →9 →8 →5
对于中序遍历,不知道大家有没有注意到,这段序列是升序的,而且这棵二叉树刚好是一棵二叉排序树,也就是说,我们可以通过中序遍历来验证二叉树是否是二叉排序树。当然这是题外话了。
在很多情况下,三种遍历的查找并没有过多的区别。
BFS层次遍历
层次遍历的想法比较朴素,而且一般通过迭代来完成,因此理解起来思路比较简单,相较于递归写法的DFS,BFS好理解一些(不需要人脑压栈模拟DFS的各个函数之间相互调用的情况),但是DFS在很多场合下是比较固定的思路,写多了容易上手,一般情况下DFS能胜任大部分的遍历工作,也有少部分比较难实现,比如说完全二叉树的判定等等。这个时候BFS就能派上用场了。
BFS层次遍历是指一层一层的遍历,以上面的二叉树为例,就是 5→1→8→0→4→7→9
这种遍历一般用到队列这种数据结构。
其实栈和队列的使用区别不大,主要是要遍历的方向。
我们的思路是通过队列来保存每一层的各个结点,每次访问一层的时候要把上一层的结点弹出,然后把新一层的结点加入其中,这样子就能达到通过循环层次遍历的效果。
talk is cheap, show me your code.
//这里用cpp STL的queue
void LevelOrderTraverse(BiTree T){
if(!T) return;
queue <BiTree *> q;
q.push(T);
for(; !q.empty(); ){
int len = q.size();
for(int i = 0; i < len; ++i)
{
BiTree * t = q.front();
q.pop();
if(t->left) {q.push(t->left);
//operations
}
if(t->right) {q.push(t->right);
//opeartions
}
}
}
}