代码随想录day24

回溯算法理论基础

什么是回溯法

● 回溯搜索法,搜索的方式
● 回溯是递归的副产品,有递归就有回溯
● 回溯函数就是递归函数

效率

● 不高效,本质是穷举
● 像高效可以加一些剪枝操作,但本质还是穷举
● 有的问题只能暴力搜索

解决问题

● 组合:N个数按规则找K个数
● 切割:字符串按规则有几种切割方式
● 子集:N个数的集合里有多少符合条件的子集
● 排列:N个树按规则全排列,有几种排列方式
● 棋盘:N皇后、解数独
● 排列和组合?
○ 组合不强调元素顺序,排列强调元素顺序

如何理解回溯法

● 回溯法解决的问题可以抽象为树形结构
● 集合的大小就是树的宽度,递归的深度是树的深度
● 递归要有终止条件,因此必须是一棵高度有限的N叉树

模板

● 回溯函数模板返回值和参数

void backtracking(参数)

● 回溯函数终止条件
○ 树是搜到了叶子节点,找到了满足的答案,存放答案,结束递归

if (终止条件) {
    存放结果;
    return;
}

● 遍历过程
○ 回溯法一般是在集合中递归搜索,集合大小构成树的宽度,递归深度构成树的深度
○ for是横向遍历,backtracking是纵向遍历

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
   处理节点;
   backtracking(路径,选择列表); // 递归
   回溯,撤销处理结果
}

77. 组合

● 力扣题目链接
● 给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
● 示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

思路

● k为1,一层循环;k为2,两层循环,再多没法表示
● 我们使用递归来解决嵌套层数的问题
● n是树的宽度,k是树的深度。每次搜索到叶子节点,我们就找到了一个结果
● 递归函数返回值和参数
○ result 存放符合条件结果集合
○ path 存放符合条件结果
○ 我们还需要一个startIndex 防止出现重复组合,取1后下一层在234取
● 回溯函数终止条件
○ 当path大小达到k,说明找到了子集大小为k的组合,保存起来即可
● 单层搜索过程
○ for进行横向遍历,递归是纵向遍历
● 剪枝优化
○ 比如n=4 k=4 其实第一层循环从2开始就没有意义了

代码

// 未剪枝
class Solution {
    List<List<Integer>> res = new ArrayList(); // 保存结果集合
    LinkedList<Integer> path = new LinkedList(); // 保存结果
    public List<List<Integer>> combine(int n, int k) {
        combineHelper(n, k, 1); // 从1开始
        return res;
    }
    private void combineHelper(int n, int k, int startIndex) {
        if (path.size() == k) { // 找到一个结果
            res.add(new ArrayList(path)); // 加入结果集合
            return;
        }
        for (int i = startIndex; i <= n; i++) { // 循环遍历树宽
            path.addFirst(i); // 加进去
            combineHelper(n, k, i + 1); // 递归遍历深度
            path.removeFirst(); // 这层遍历完了要弹出来。再继续下一个同层的遍历
        }
    }
}
// 加了剪枝操作
class Solution {
    List<List<Integer>> res = new ArrayList();
    LinkedList<Integer> path = new LinkedList();
    public List<List<Integer>> combine(int n, int k) {
        combineHelper(n, k, 1);
        return res;
    }
    private void combineHelper(int n, int k, int startIndex) {
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        // n = 4, k = 2, 这时size = 0, 那么从index = 3开始可以,后面的就不行了
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {
            path.addFirst(i);
            combineHelper(n, k, i + 1);
            path.removeFirst();
        }
    }
}

你可能感兴趣的:(代码随想录,java,算法,开发语言)