【LeetCode】十四、回溯法:括号生成 + 子集

1、回溯法

在这里插入图片描述

使用场景,如找[1,2,3]的所有子集:

在这里插入图片描述

2、leetcode22:括号生成

在这里插入图片描述

以n=2为例,即两个左括号、两个右括号,枚举所有的情况:
在这里插入图片描述
从左往右数,当左括号的数量 < 右括号的数量时,就不是一个有效的括号,比如:

//一开始数到第一个就出现左括号的数量 < 右括号的数量
)()(
// 数到第三个时,左括号的数量 < 右括号的数量,无效
())(

如此,枚举所有可能性的过程中,如果出现左括号的数量 < 右括号的数量,则说明此路不通,终止递归,回溯到上一步重新选择

public class P22 {

    public List<String> generateParenthesis(int n) {
        List<String> result = new ArrayList<>();
        backTracking(n, result, 0, 0, "");
        return result;
    }

    /**
     *
     * @param n 需要的括号的数量
     * @param result 存结果,有合适的结果就塞进result
     * @param left 左括号的数量
     * @param right 右括号的数量
     * @param str 左右括号的分隔符,比如” “
     */
    public static void backTracking(int n, List<String> result, int left, int right, String str) {
        // 右括号的数量大于左括号的数量了,说明是无效括号,此路不通,终止递归
        if (right > left){
            return;
        }
        // 左括号的数量等于右括号的数量,等于要求的数量,说明找到结果了,加入结果集,终止递归
        if (left == right && right == n){
            result.add(str);
            return;
        }

        // 左括号的数量小于要求的,可以加个左括号
        if (left < n) {
            backTracking(n, result, left + 1, right, str + "(");
        }

        // 右括号的数量小于要求的,可以加个右括号
        if (right < n) {
            backTracking(n, result, left, right + 1, str + ")");
        }

    }
}

3、leetcode78:子集

在这里插入图片描述

解法一:扩展法

以空集开始,遍历给定集合的每个元素,并把上面每一层的结果和当前元素相加。比如给定[1,2,3]

在这里插入图片描述

比如上面,遍历到3时,把3并入到前面三层的结果集:

第一层结果:[]
第二层结果:[1]
第三层结果:[2]  [1,2]

就得到了第四层:[3] 、[1,3]、 [2,3] 、[1,2,3]。代码实现:

public class P78 {

    public static List<List<Integer>> subsets(int[] nums) {
        if (nums == null) {
            return null;
        }
        List<List<Integer>> result = new ArrayList<>();
        // 空集这个子集
        result.add(new ArrayList<>());
        for (int num : nums) {
            List<List<Integer>> temp = new ArrayList<>();
            for (List<Integer> element : result) {
                // 不能直接修改element,否则会改变结果集的元素
                // 用一个同类型的对象,拷贝一份,防止发生引用传递
                List<Integer> copy = new ArrayList<>(element);
                // 将给定数组的一个个元素,并入结果集的每个元素
                copy.add(num);
                // 存入临时结果集,别放入最终结果集,这样result一直变,循环就停不下了
                temp.add(copy);
            }
            // 一层遍历完了,把结果放进最终的结果集,准备将给定数组的下一个元素分别并入结果集,得到新的子集
            temp.stream().forEach(e -> result.add(e));
        }
        return result;
    }
}

以上注意:遍历前面的结果集里的每个元素时,用一个copy对象,防止引用传递。测试结果与分析时所画的顺序也一致:

在这里插入图片描述

解法二:回溯法

以[1,2,3]为例,思路:

  • 其子集的长度可能有:0、1、2、3
  • 按这四种可能长度循环,每次找到符合长度的的子集,就放入结果集
public class P78 {

    public static List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        // 长度为0的子集:空集,先放入
        result.add(new ArrayList<Integer>());
        // 子集长度还可能是1~给定的数组长度
        for (int i = 1; i <= nums.length; i++) {
            backTracking(nums, result, i, 0, new ArrayList<>());
        }
        return result;

    }

    public static void backTracking(int[] nums, List<List<Integer>> result, int length, int index, List<Integer> subset) {
        // 递归的终止条件:找到了满足长度的子集,加入结果集,停止递归
        if (subset.size() == length) {
            List<Integer> copy = new ArrayList<>(subset);
            result.add(copy);
            return;
        }
        for (int i = index; i < nums.length; i++) {
            subset.add(nums[i]);
            backTracking(nums, result, length, i+1, subset);
            // 找到了[1,2],回溯,下一个该[1,3]了,这里remove掉2,以免下一个出现[1,2,3]
            subset.remove(subset.size() - 1);
        }
    }
}

解法三:DFS深度优先算法

public class P78 {

    public static List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        dfs(nums, result, 0, new ArrayList<>());
        return result;
    }

    public static void dfs(int[] nums, List<List<Integer>> result, int index, List<Integer> subset) {
        List<Integer> copy = new ArrayList<>(subset);
        result.add(copy);
        if (index == nums.length) {
            return;
        }
        for (int i = index; i < nums.length; i++) {
            subset.add(nums[i]);
            dfs(nums, result, i+1, subset);
            subset.remove(subset.size() - 1);
        }
    }
}

相关推荐

  1. Leetcode78.子集 - Subset - Python - 回溯

    2024-07-18 02:08:01       43 阅读
  2. Leetcode 90.子集II - Subset II - Python - 回溯

    2024-07-18 02:08:01       43 阅读
  3. leetcode热题HOT 22. 括号生成(回溯)

    2024-07-18 02:08:01       34 阅读
  4. 回溯Leetcode 78. 子集【中等】

    2024-07-18 02:08:01       32 阅读
  5. LeetCode 22 括号生成

    2024-07-18 02:08:01       62 阅读
  6. [leetcode] 22. 括号生成

    2024-07-18 02:08:01       58 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 02:08:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 02:08:01       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 02:08:01       58 阅读
  4. Python语言-面向对象

    2024-07-18 02:08:01       69 阅读

热门阅读

  1. 入门c语言DAY4.1——scanf&printf详细介绍

    2024-07-18 02:08:01       24 阅读
  2. 【C#】Array和List

    2024-07-18 02:08:01       21 阅读
  3. qt设置窗口位置设置

    2024-07-18 02:08:01       22 阅读
  4. bs4取值技巧的详细介绍

    2024-07-18 02:08:01       22 阅读
  5. Llama - Prompting

    2024-07-18 02:08:01       21 阅读
  6. 【SASS/SCSS(二)】模块化语法

    2024-07-18 02:08:01       26 阅读
  7. HTML5应用的安全防护策略与实践

    2024-07-18 02:08:01       22 阅读
  8. 23种设计模式

    2024-07-18 02:08:01       20 阅读
  9. tomcat如何进行调优?

    2024-07-18 02:08:01       17 阅读
  10. C#调用非托管dll的两种方式

    2024-07-18 02:08:01       22 阅读