【40讲系列9】回溯算法、剪枝

一、理论

  1)首先,使用回溯算法关键是,将问题转化为 【树形问题】

  2)回溯的关键点: for循环、 递归

   for循环的作用在于另寻它路,可以逐个选择当前节点下的所有可能往下走下去的分支路径。

   递归可以实现一条路走到黑和回退一步,把递归放在for循环内部,那么for每一次的循环,都在给出一个路径后进入递归,继续往下走。

  3)因此,for循环和递归配合可以实现回溯,所以DFS是最典型的回溯法的应用。

 

  理论详解及分类经典习题:回溯算法入门级详解 + 练习(持续更新)

二、典型例题

☆☆☆☆①:如何使N个皇后彼此之间不能相互攻击(LC51、LC52)

思路:如果在(i,j)处放置了一个皇后,那么

1. 整个第 i 行的位置都不能放置;

2. 整个第 j 列的位置都不能放置。

3. 如果位置(a,b)满足|a-i|==|b-j|,说明(a,b)和(i,j)处在同一条斜线上,也不能放置。

class Solution {
    // LeetCode 52
    // 参考左神,还可以进一步使用位运算加速
    int res = 0;
    public int totalNQueens(int n) {
        if (n < 1) return 0;
        int[] record = new int[n]; // record[index]表示第index行皇后所在的列数
        help(record,0,n);
        return res;
    }
    private void help(int[] record,int index, int n){
        if (index == n){ // 此时已经确定一种情况
            res++;
            return;
        }
        for (int j = 0; j < n; j++) {
            if (isVaild(record,index,j)){
                record[index] = j;
                help(record,index+1,n);
            }
        }
    }
    private boolean isVaild(int[] record,int index, int j){
        for (int k = 0; k < index; k++) {
            if (record[k] == j || Math.abs(k - index) == Math.abs(record[k] - j)){
                return false;
            }
        }
        return true;
    }
}

Note:本题的最优解是使用位运算加速,见【40讲系列11】位运算

 

②:判断数独是否有效(LC36)

M

☆☆☆☆③:解数独(LeetCode37. 解数独

 

三、扩展例题

第一组(树形问题):LeetCode17. 电话号码的字母组合LeetCode93. 复原IP地址LeetCode131. 分割回文串

第二组(排列问题):LeetCode46. 全排列LeetCode47. 全排列 II

第三组(组合问题):LeetCode77. 组合LeetCode39. 组合总和LeetCode40. 组合总和 IILeetCode216. 组合总和 IIILeetCode78. 子集LeetCode90. 子集 IILeetCode401. 二进制手表

第四组(二维平面上的回溯):LeetCode79. 单词搜索

第五组(Flood Fill):LeetCode200. 岛屿数量LeetCode130. 被围绕的区域LeetCode417. 太平洋大西洋水流问题

第六组(游戏问题):LeetCode51. N 皇后LeetCode52. N皇后 IILeetCode37. 解数独

四、综合练习

题型一:排列、组合、子集相关问题

  Note:理解为什么有时候用visited数组,有时候设置搜索起点start

  • LeetCode46. 全排列
  • LeetCode47. 全排列2:思考为什么造成了重复,如何在搜索之前就判断这一支会产生重复;
  • LeetCode39. 组合总和
  • LeetCode40. 组合总和2
  • LeetCode77. 组合
  • LeetCode78. 子集
  • LeetCode90. 子集2 : 剪枝技巧同 47 题、39 题、40 题;
  • LeetCode60. 第k个排列 : 利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点;
  • LeetCode93. 复原IP地址

题型二:Flood Fill

  Note:以下问题都不建议修改输入数据,设置visited数组是标准做法。

  • LeetCode733. 图像渲染
  • LeetCode200. 岛屿数量
  • LeetCode130. 被围绕的区域
  • LeetCode79. 单词搜素

题型三:字符串中的回溯问题

  Note: 字符串问题的特殊之处在于,字符串的拼接会生成新对象,因此没有显示【回溯】的过程。但如果用StringBuilder拼接字符串则需要回溯。

  • LeetCode17. 电话号码的字母组合
  • LeetCode784. 字母大小写全排列
  • LeetCode22. 括号生成:本题也可用BFS,可以通过本题理解为什么回溯算法都是DFS,并且都用递归来写。

题型四:游戏问题

  • LeetCode51. N皇后:其实就是全排列问题,注意设计清楚状态变量,在遍历的时候需要记住一些信息,空间换时间;
  • LeetCode37. 解数独:思路同N皇后问题
  • LeetCode488. 祖玛游戏
  • LeetCode529. 扫雷游戏

 

posted @ 2020-11-22 21:27  不学无墅_NKer  阅读(649)  评论(0编辑  收藏  举报