DFS-回溯

递归

一个函数调用自身,这样的方式就是递归。

引入题:井字格,如果不走远路的话,有多少种走法?最核心的部分:f(m,n)=f(m-1, n)+f(m,n-1),注意剔除特殊点。

private static int f(int m, int n) {
    if (m < 0 || n < 0) {
        return 0;
    }
    if (m == 0 && n == 0) {
        return 1;
    }

    return f(m - 1, n) + f(m, n - 1);
}

分治法(Divide Conquer):看上面的公式,可以发现缩小了问题的空间,这种思想就是分治。

递归可能性能不是很高,可以通过一个 Count 来记录计算的次数,用来观察。

通过加 Cache 的方法,可以优化解法,让复杂度大大降低。

public class Solution {
    static int count = 0;
    static int[][] cached = new int[100][100];

    public static void main(String[] args) {
        System.out.println(f(3, 2));
    }

    private static int f(int m, int n) {
        count++;
        if (m < 0 || n < 0) {
            return 0;
        }
        if (m == 0 && n == 0) {
            return 1;
        }
        if (cached[m][n] != 0) {
            return cached[m][n];
        }
        cached[m][n] = f(m - 1, n) + f(m, n - 1);
        return f[m][n];
    }
}

动态规划

上面的题目也可以用动态规划的解法来解。因为,可以从起点开始,先把显而易见的结果准备好,然后根据已经得到的结果,进行推导出后续点的结果。

  1. 把显而易见的结果放在那边——数据准备好

  2. 按照规划好的路线,根据已经准备好的数据,继续计算

从显而易见的结果,能够规划出向目标推进的一条路线,利用已经获得的结果进行推算,一直推算到最后的结果,叫动态规划。

DFS

如果上面的题目条件做了改变:不一定要走近路,可以绕着走,只要不走过同一个路口,这样有多少种路径?

深度搜索是一种很重要的解法,强相关的是回溯法解法。BFS 相对出现频率相对较少。

回溯是与 DFS 强相关的一种编程技巧。用递归来实现回溯,是一种常见的写法。

在每个点上,规定一个探路的规矩,顺时针方向。 探路的过程,为了找到全部的路线,在找到一条路线后,需要返回,然后继续尝试。

深度优先搜索,考虑的出发点有 2 点: 1. 深度:在本问题中,往前走一步可以就看做深度上加了一层;—— 考虑函数的设计、参数的定义 2. 广度:有多少种选项,四个方向。选项执行不能重复,要看满足什么条件时,走那种选项。亦或者,项循环去执行每种选项。

回溯有一个重要点,执行完递归时,回到一开始起点时,状态要保持不变。因此,可以留意递归内部的参数,如果参数是有改变的场景,那么,调用函数自身的上下应该会有恢复状态的操作。

比如 leetcode 46 道题中,递归时调用方法自身,方法中的参数是列表,传进去之前修改了这个参数值,那么,下面一行会有恢复状态的操作。

比如 leetcode 22 括号生成这道题,调用方法本身进入递归时,传入的参数是在传入时内部进行的计算,而不是进去之前进行的操作,因此,就没有恢复状态的步骤。

如果上述 22 题改成如下写法,那么可以看到恢复状态的步骤了:

回溯练习题

典型例题

最后更新于

这有帮助吗?