【算法基础】深度优先搜索(DFS)

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 ,返回它节点值的 前序 遍历。

【算法基础】深度优先搜索(DFS)_第1张图片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 &res) {
        if (root == nullptr) {        //basecase
            return;
        }
        res.push_back(root->val);
        preorder(root->left, res);
        preorder(root->right, res);
    }

    vector preorderTraversal(TreeNode *root) {
        vector res;
        preorder(root, res);
        return res;
    }
};

4.2 例题2

【leetcode-104】给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

【算法基础】深度优先搜索(DFS)_第2张图片

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,这个递归就只有“递”,不会“归”。

你可能感兴趣的:(算法基础,算法,深度优先,c++,dfs,leetcode)