1 定义
深度优先搜索(Depth-First Search,DFS),是一种通过暴力枚举每一条路径的方法,来遍历树或图的所有路径,其中,每个节点只能访问一次。DFS通常是由递归来实现的,可以用来处理遍历整张图、求问题有多少个解、多少个节点、多少条路经等。可理解为是一种“一条路走到黑”的算法,最糟糕的时间复杂度可达到O(n!)。
2 算法思想
回溯法:也可以称为试探法,程序按照优选条件向前探索,但是在到达某一结点时,发现该节点并不是最优解,故此时就要回退一步或多步重新选择节点,这样回退重选的过程称为回溯。
3 具体步骤
1 创建结果储存变量,初始化当前结果;
2 设计递归函数
-如达到结果,则返回(basecase)
-如未达到结果,则更新当前结果
-对当前节点使用该递归函数
3 开始调用递归函数
模板代码:
int check(参数)
{
if(满足条件) //basecase
return 1;
return 0;
}
void dfs(int step)
{
判断边界
{
相应操作
}
尝试每一种可能
{
满足check条件
标记
继续下一步dfs(step+1)
恢复初始状态(回溯的时候要用到)
}
}
4.1 例题1
【leetcode-144】给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
4.1.1大致思路
二叉树的前序遍历指的是按照访问根节点——左子树——右子树的方式遍历这棵树。以上图为例,遍历的结果就是:1 2 4 5 7 3 6
我们在访问左子树或右子树的时候,我会用到同样的方式来遍历,这似乎是一个“循环”,(即让计算机重复相同的操作),直到遍历到这棵树的叶节点,所有,我们可以使用递归来实现遍历。
定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root.left) 来遍历 root 节点的左子树,最后递归调用 preorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点
4.1.2 代码实现
class Solution {
public:
void preorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) { //basecase
return;
}
res.push_back(root->val);
preorder(root->left, res);
preorder(root->right, res);
}
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
preorder(root, res);
return res;
}
};
4.2 例题2
【leetcode-104】给定一个二叉树 root
,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
4.2.1 大致思路
如果我们分别知道该二叉树的左子树和右子树的最大深度l、r,那么该二叉树的最大深度为max(l,r)+1 。后面的那个1是root节点本生的长度。
而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用「深度优先搜索」的方法来计算二叉树的最大深度。
4.2.2 代码实现
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0; //basecase
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
5 一些个人理解(仅代表个人观点,欢迎指出错误)
对于递归,我们可以将它理解为一个全自动帮程序员处理数据的手段,也就是说,你遇到了什么问题,交给递归,它会帮你刨根溯源,在这里,怎么样才算是刨到了根呢?这就体现出basecase的重要性了。没有basecase,这个递归就只有“递”,不会“归”。