【二叉树】二叉树的深度优先遍历DFS(前中后序遍历)和广度优先遍历BFS(层序遍历)详解【力扣144,94,145,102】【超详细的保姆级别教学】
【二叉树】二叉树的深度优先遍历(前中后序遍历)和广度优先遍历(层序遍历)详解【超详细的保姆级别教学】
先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️
这篇博客包含了我们出血二叉树时最先学的,也是最基础的知识点:二叉树的遍历。
本篇建议收藏后食用~
博主这里提供题目的传送门:学完的伙伴可以顺便去力扣把题给做了。但是我们要注意的是,学会这些遍历方法才是重点,而不是单纯地把题目做完。
树的结构
//取自leetcode
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
深度优先遍历(DFS)
什么是深度优先遍历
深度优先遍历:
指的是,遍历的时候,向二叉树的深度先遍历,一般采用的方式是递归,即遇到二叉树底层才回溯。
而深度优先遍历包括:前序,中序和后序遍历。
如:前序遍历:
如图:在这颗二叉树中:
我们在深度优先遍历中,往深度先遍历。因此以前序遍历为例来说,遍历顺序就是A-B-D-E-C,我们是先往下走,至于具体怎么遍历,我在下面会详细讲。
广度优先遍历:
层序遍历就是一种广度优先遍历,这种方法在遍历二叉树的时候,是一层一层来的,也就是,往宽的方向走。如上图例子:遍历顺序就是A-B-C-D-E。
关于DFS深度优先遍历,我们可以使用递归法和非递归法,这里展示的是递归法,以后博主可能还会补充非递归法的过程。伙伴们持续关注哦~
前序遍历
核心:
遍历顺序:根-左子树-右子树
注意:此处不是根-左子结点-右子结点,而是根-左子树-右子树。
如图:让博主带着大家详细地介绍这个前序遍历,明白了这个前序,中序和后序都是相类似的。
在这幅图中:我们按照根-左-右的顺序来,遇到空就回溯,在这里,如果对函数递归不熟练的伙伴,建议先将函数递归弄明白,再继续食用。
首先我们从根开始,也就是1,1的左是否为空,不为空。所以1的左子树,也就是2为根左边那棵树,所以我们找到2
此时的顺序是1-2
2的左边为空吗?不为空,所以找到4,此时为1-2-4
4的左边为空吗?为空,所以回溯,回到4,此时为1-2-4-null
回到4后,看4的右边,为空吗?不为,所以1-2-4-null-8
8的左为空吗,为空。右为空吗,为空。此时为1-2-4-null-8-null-null。
两边都为空,我们要回溯,4的左右我们都看了,所以回到2
2的右边为空吗?不为,找到5,所以此时1-2-4-null-8-null-null-5.
…
看到这里我们应该可以继续推出后面的遍历顺序了
最后的结果是:
1-2-4-null-8-null-null-5-null-null-3-6-9-null-null-10-null-null-7-null-null
大家可以对一下答案,看看自己算对没有哦。
知道思路,我们可以写代码了:
class Solution {
public:
//前序遍历
void preordertraversal(TreeNode* cur, vector<int>& vec) {
//确定终止条件
if (cur == NULL) {
return;
}
//确定单层递归的逻辑
vec.push_back(cur->val);
preordertraversal(cur->left, vec);
preordertraversal(cur->right, vec);
}
};
中序遍历和后序遍历
中序遍历和后序遍历其实和前序遍历大部分是一样的。
中序遍历顺序:左子树-根-右子树
后序遍历顺序:左子树-右子树-根
关于具体怎么遍历,和前序的思路其实是一样的,只是顺序换一下,这里就不赘述了。
看代码我们可以发现,其实就是取节点和两个递归函数之间的顺序变了而已,其它都是一样的。
中序遍历:
//中序遍历
class Solution {
public:
void inOrder(TreeNode* root, vector<int>& res) {
if (!root) {
return;
}
//就是下面三者顺序调整一下而已
inOrder(root->left, res);
res.push_back(root->val);//放中间
inOrder(root->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
};
后序遍历:
//后序遍历:
class Solution {
public:
void postOrder(TreeNode*root,vector<int>&ret){
if(root==NULL)return;
postOrder(root->left,ret);
postOrder(root->right,ret);
ret.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int>ret;
postOrder(root,ret);
return ret;
}
};
整个深度优先遍历,其实就是递归遍历:
广度优先遍历(BFS)
非递归遍历
二叉树的层序遍历需要用到队列的知识,暂时没学过队列的伙伴可以通过传送门先学习队列,再继续食用本篇~【栈和队列】纯C实现栈和队列以及其基本操作-宝藏级别数据结构教程【保姆级别详细教学】
层序遍历的思路比较简单,但实现过程稍微难一点
思路:即一层一层访问:
如何实现呢?
我们要通过队列来帮助我们实现
过程为:
图片取自百度:
借助队列的先进先出
核心思路是:上一层带下一层
访问到数据的时候,push()
进队列里面,然后在访问下一层的同时将上一层的pop()
出去一个值。最后当队列为空的时候,我们结束遍历。按照动图所示,我们就可以完成一层一层的遍历了。具体如动图所示。
层序遍历:
//结果插入vector中
class Solution {
private:
vector<vector<int>>ret;
public:
vector<vector<int>> levelOrder(BTNode* root) {
if (!root) {
return ret;
}
queue<BTNode*>q;//队列存地址
q.push(root);
while (!q.empty()) {//队列为空,结束遍历
int curentLevalSize = q.size();
ret.push_back(vector <int>());//这里指的是二维数组的行地址
for (int i = 0; i < curentLevalSize; i++) {
BTNode* node = q.front();//先保存队头元素,否则弹走了就找不到了
q.pop();//每次都要弹出一个元素
ret.back().push_back(node->data);//将数据push进结果数组里
//如果左边不为空,把左边push进队列
if (node->left) {
q.push(node->left);
}
//如果右边不为空,把右边push进队列
if (node->right) {
q.push(node->right);
}
}
}
return ret;
}
};
尾声
相信大家看到这里,已经对二叉树的DFS,BFS遍历有了一定的认识和了解了。这些都是我们学习数据结构最最基础的东西,也是必不可少的知识,我们必须掌握。如果你感觉这篇文章对你有帮助的话,不要忘了点赞关注和收藏哦!