二叉树的遍历
我又又又又又来啦。这次我将带大家深入理解二叉树的4种遍历方式。大家赶紧先想一想是哪4种方式吧!
好的,就让我来揭晓答案吧!
- 先序遍历
- 中序遍历
- 后序遍历
- 层序遍历
在开始遍历前,我们需要进行几个准备。
- 给出树结构的声明
- 给出四种遍历函数的声明
- 包含遍历所需要容器所在的文件
在这些准备工作都已经就绪后,就要开始我们的第一个遍历也就是先序遍历啦。作为第一个,当然也是最简单的,请大家要专注,跟着我一起深入理解噢!
先在头文件中给出四个函数的声明
void PreOrderTraversal(TreeNode *BT);
void InOrderTraversal(TreeNode *BT);
void PostOrderTraversal(TreeNode *BT);
void LevelOrderTraversal(TreeNode *BT);
1.先序遍历
先序遍历的顺序是根节点->左节点->右节点,其实啊,这个前中后遍历就是根据根节点遍历的次序来确定名字的,先序遍历就是把根节点先遍历,大家是不是突然明白了点什么呢!
这里由于树可以通过递归来定义,所以树的一些操作啊大多都是通过递归来实现的,就比如求某一条路径总和啊,路径总和最大值啊,最先公共祖先啊······(扯远了~)
好!既然讲到了递归,大家有没有想起来一种执行方式跟递归非常相似的一种数据结构呢?没错!就是那什么!
栈,也称堆栈(曾经去了解过为什么加个堆字,后面好像看到说念起来好听emmm)实际上函数的递归使用,跟栈的实现原理是一样的,在我们使用过递归后,希望童鞋们可以再试试通过栈来实现遍历,这将非常有助于你对遍历过程的了解,但可能一时半会理解不了,没有关系!大家都是这么过来的,希望大家多花些时间!
void PreOrderTraversal(TreeNode *BT) {
if (BT) {
printf("%d\n", BT->Data);
PreOrderTraversal(BT->lChild);
PreOrderTraversal(BT->rChild);
}
}
哇,同学们是不是发现,怎么就几行就完成了啊,那么接下来让我们看看栈的实现,栈的实现过程就是对这个递归过程的详细解读。
stack<TreeNode*> mystack;
while (BT || !mystack.empty()) {
while (BT) { /*一直向左并将沿途结点打印后入栈*/
printf("%d\n", BT->Data);
mystack.push(BT);
BT = BT->lChild;
}
if (!mystack.empty()) {
BT = mystack.top();
mystack.pop();
BT = BT->rChild;
}
}
这里是把函数里面的内容全部替换成了这一段噢,童鞋们可要注意咯~
童鞋们慢慢品味里面的内容吧~ 接下来就会给出其他几个方式的代码了噢,中序遍历和后序遍历博主不太想讲了噢,因为就跟先序遍历一样的原理,只是遍历顺序改变了噢~
2.中序遍历
void InOrderTraversal(TreeNode *BT) {
if (BT) {
TreeTraversal::InOrderTraversal(BT->lChild);
printf("%d\n", BT->Data);
TreeTraversal::InOrderTraversal(BT->rChild);
}
}
同样也能使用栈噢~
stack<TreeNode*> mystack;
while (BT || !mystack.empty()) {
while (BT) {
mystack.push(BT);
BT = BT->lChild;
}
if (!mystack.empty()) {
BT = mystack.top();
mystack.pop();
printf("%d\n", BT->Data);
BT = BT->rChild;
}
}
3.后序遍历
void PostOrderTraversal(TreeNode *BT) {
if (BT) {
TreeTraversal::PostOrderTraversal(BT->lChild);
TreeTraversal::PostOrderTraversal(BT->rChild);
printf("%d\n", BT->Data);
}
}
诶~到了这里,栈的应用与上面两种的差别就会大了一点噢。
要实现访问顺序为左节点->右节点->根节点,可以通过栈按照根节点、右节点、左节点的顺序压栈。所以我们要做的就是将先序遍历的左右先后顺序交换,然后通过另一个栈实现访问方式的打印。但缺点是空间占用较大,下面给出代码
stack<TreeNode*> mystack1;
stack<TreeNode*> mystack2;
while (BT || !mystack1.empty()) {
while (BT) {
mystack1.push(BT);
mystack2.push(BT);
BT = BT->rChild;
}
if (!mystack1.empty()) {
BT = mystack1.top();
mystack1.pop();
BT = BT->lChild;
}
}
while (!mystack2.empty()) {
BT = mystack2.top();
mystack2.pop();
printf("%d\n", BT->Data);
}
4.层序遍历
其实啊,二叉树遍历的核心问题是二维结构的线性化。在通过结点访问了左节点后,右节点应该如何访问呢?前面三种我们非递归的方式是通过堆栈实现的,事实上我们也能通过队列来保存。
这里就要讲一下队列实现的基本思路了:遍历从根节点开始,先根节点入队,然后执行循环:结点出队,访问根节点,左节点入队,右节点入队,直到队列为空的时候停止。
这种遍历的结果是将二叉树从上到下,从左至右一层一层遍历,即层序遍历,下面给出代码啦~
void LevelOrderTraversal(TreeNode *BT) {
if (BT == nullptr) return;
queue<TreeNode*> team;
team.push(BT);
while (!team.empty()) {
BT = team.front();
team.pop();
printf("%d\n", BT->Data);
if (BT->lChild) team.push(BT->lChild);
if (BT->rChild) team.push(BT->rChild);
}
}
好啦,四种二叉树的遍历就已经讲完啦~同学要是有什么问题可以给我留言或者评论噢,记得光看不行,还得练!!!可以去PAT上找一找类似题目做做噢,随时期待大家的提问噢!