【LeetCode】升级打怪之路 Day 24:回溯算法的解题框架

今日题目:

目录

      • [LC 46. 全排列](#LC 46. 全排列)
      • [LC 51. N 皇后](#LC 51. N 皇后)
      • [LC 78. 子集 【classic】](#LC 78. 子集 【classic】)

今天学习了回溯算法的解题框架:回溯算法解题套路框架 | labuladong

回溯算法的整体框架都是:

python 复制代码
result = []

def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

基本上所有使用回溯算法来解题时都是这个思路,区别就在于要根据具体题目背景来设计"选择列表"有什么、如何做出选择等等

在我们做题时,简单画一下回溯算法的决策树能够快速帮助我们如何设计"遍历选择列表"、"做选择"等这些操作。可以参考这篇文章的 LC 78 题目。

LC 46. 全排列

46. 全排列

学会回溯算法的思路后,这个题目就不难了,看看解题框架是如何运用到这个题目中的:

java 复制代码
class Solution {

    private List<List<Integer>> result;

    private void backtrack(List<Integer> path, int[] nums, boolean[] used) {
        // 加入结果集
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
        }

        // 遍历选择
        for (int i = 0; i < nums.length; i++) {
            if (used[i]) {
                continue;
            }
            // 做出选择
            used[i] = true;
            int num = nums[i];
            path.addLast(num);
            backtrack(path, nums, used);
            // 撤销选择
            used[i] = false;
            path.removeLast();
        }
    }

    public List<List<Integer>> permute(int[] nums) {
        result = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        boolean[] used = new boolean[nums.length];
        Arrays.fill(used, false);
        backtrack(path, nums, used);
        return result;
    }
}

LC 51. N 皇后

51. N 皇后

这也是经典的使用回溯来解题的题目:每一次在新的一行中选出一列放上棋子,直到 n 行都放上棋子后就可以作为一种答案。

这个题目在使用回溯算法时,关键难点在于如何判断在做出一个选择后的棋局是否满足 N 皇后要求。因为我们是每次在新的一行中放入棋子,所以只需要检测这个新的棋子是否存在列冲突以及斜线冲突。

按照以上思路,题目也就不难了。

LC 78. 子集 【classic】

78. 子集

这个题目有两种思路来画回溯算法的决策树:

1)思路一

每一次做选择时,是从当前节点元素在 nums 后面的元素中选一个加入到路径中。在遍历这个决策树时,每一步都将其加入到结果集中,就可以得到所有的子集了。

2)思路二

决策树的第 i 层所做的选择是:nums[i] 是否使用。然后叶子节点就对应了一个结果集中的答案。决策树如下:

这里看一下两种思路的代码:

  • 思路一代码:
java 复制代码
class Solution {

    private List<List<Integer>> result;

    private static List<Boolean> choices = List.of(false, true);

    private void backtrace(List<Integer> path, int[] nums, int start) {
        result.add(new ArrayList<>(path));
        for (int i = start; i < nums.length; i++) {
            path.addLast(nums[i]);
            backtrace(path, nums, i + 1);
            path.removeLast();
        }
    }

    public List<List<Integer>> subsets(int[] nums) {
        result = new ArrayList<>();
        backtrace(new ArrayList<>(), nums, 0);
        return result;
    }
}
  • 思路二代码:
java 复制代码
class Solution {

    private List<List<Integer>> result;

    private static List<Boolean> choices = List.of(false, true);

    private void backtrace(List<Integer> path, int[] nums, int level) {
        if (level == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }

        // 遍历选择列表
        for (var choice: choices) {
            int num = nums[level];
            if (choice) {
                path.addLast(num);
                backtrace(path, nums, level + 1);
                path.removeLast();
            } else {
                backtrace(path, nums, level + 1);
            }
        }
    }

    public List<List<Integer>> subsets(int[] nums) {
        result = new ArrayList<>();
        
        if (nums.length == 0) {
            return List.of(Collections.emptyList());
        }
        
        backtrace(new ArrayList<>(), nums, 0);

        return result;
    }
}
相关推荐
Lenyiin8 分钟前
01.02、判定是否互为字符重排
算法·leetcode
鸽鸽程序猿24 分钟前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd24 分钟前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo61728 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
乐之者v33 分钟前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神2 小时前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人2 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
半盏茶香2 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
忘梓.3 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(3)
算法·动态规划