DFS 解题套路

DFS 解题套路

如果碰到一个 DFS 题目,基本的代码套路如下:

1、我们基本都是使用递归来解决 DFS 问题,因此要确定递归的结束条件,也就是 DFS 结束条件。

2、写 DFS 函数套路框架。如下所示

void dfs() {
}

int main() {
    读入数据

    dfs();

    return 0;
}

3、确认 DFS 函数的参数。这个是最难的地方,需要根据具体的题目来确定。一般来说,我们根据题目的搜索状态来确认这个。

4、如果有回溯,需要保证每次 DFS 完成后能恢复原状态,这样搜索回溯才不会有错误。

举例

下面我们来几个例子解释一下。

LeetCode 784 字母大小写全排列

题目链接为https://leetcode-cn.com/problems/letter-case-permutation/

输入: S = "a1b2"
输出: ["a1b2", "a1B2", "A1b2", "A1B2"]

按照题目的要求,我们知道任何一个字母将有两条路径可能:路径 1,字母变小写;路径 2,字母变大写。如下图所示:

因此上面的样例有 2 个字母,可能性就是 2*2=4 种。

搜索终止条件

终止条件必然是搜索到了字符串的长度。

搜索函数参数

下面我们来分析 dfs() 函数需要哪些参数。由于得到的结果是字符串,因此 dfs() 函数中,

1、需要传入一个字符串,表示当前的字符是怎么样的。

2、还需要一个位置,表示当前枚举到字符串的哪个地方,即字符串的第几位。

所以我们可以确定本题的 dfs() 函数原型应该是如下所示:

//参数s:表示当前字符串的内容
//参数index:表示现在枚举到字符串s的第几位
void dfs(string &s, int index) {
    if (index==s.length()) {
        return;
    }
    dfs(s, index+1);//保持不变直接搜索下一位
    if (s[index]>='A') {
        //是字母
        s[index] ^= 32;//大小写切换
        dfs(s, index+1);//搜索下一位
    }
}

开始调用方式

dfs(s, 0);//表示从第0位开始搜索字符串s

回溯

本题不需要回溯,直接搜索就可以了。

AC 参考代码

class Solution {
public:
    vector<string> ans;

    vector<string> letterCasePermutation(string S) {
        dfs(S, 0);
        return ans;
    }

    void dfs(string &s, int idx) {
        if (s.length()==idx) {
            ans.push_back(s);
            return;
        }

        //本位不变,搜索下一位
        dfs(s, idx+1);

        //本位变,搜索下一位
        if (s[idx]>='A') {
            s[idx] ^= 32;
            dfs(s, idx+1);
        }
    }
};

LeetCode 77 组合

题目链接为https://leetcode-cn.com/problems/combinations/。输入整数 n 和 k,得出 1 ~ n 个数中 k 个数字的组合。

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

本题的思路就是在 1 ~ n 个数字中,从小到大的选出 k 个数字,这样答案一定是确定的。

搜索终止条件

终止条件必然是搜索出的数字个数达到 k。

搜索函数参数

1、一个数组,表示现在已经搜索出几个数字。

2、一个整数表示开始搜索的数字 start。

3、一个整数表示搜索的终止数 n。这个可以用全局变量来表示,如果用全局变量这个参数可以省略。

4、还剩下几个数需要搜索 left。

因此,本题的 dfs() 函数的原型可以是如下的表示:

//参数1:path存放已经搜索出了几个数据
//参数2:start表示从哪个数字开始搜索
//参数3:n表示搜索的最大数据
//参数4:left还剩下几个数没有搜索到
void dfs(vector<int> &path, int start, int n, int left);

开始调用方式

//从0开始搜索,搜索到数据n,一共搜索k个数字
dfs(path, 0, n, k);

回溯

        path.push_back(i);//将数据i保存到path中
        dfs(path, i+1, n, k-1);
        path.pop_back();//回溯

AC 参考代码

class Solution {
public:
    vector<vector<int>> ans;//答案

    vector<vector<int>> combine(int n, int k) {
        vector<int> path;

        dfs(path, 1, n, k);

        return ans;
    }

    void dfs(vector<int> &path, int start, int n, int k) {
        if (0==k) {
            //搜索到了
            ans.push_back(path);
            return;
        }

        for (int i=start; i<=n; i++) {
            //加入当前数据
            path.push_back(i);
            dfs(path, i+1, n, k-1);
            path.pop_back();//回溯
        }
    }
};

虽然这两个例子都非常的简单,但是希望通过对这样简单的例子分析,能给大家建立编写 DFS 程序的模板套路。

发布了239 篇原创文章 · 获赞 291 · 访问量 107万+

猜你喜欢

转载自blog.csdn.net/justidle/article/details/104925699
dfs
今日推荐