当前位置: 首页 > article >正文

代码随想录day25 | leetcode 491.递增子序列 46.全排列 回溯总结

考试周连考不复习就挂科了 一直没更新十分抱歉 今天开始在周日前补回来

491.递增子序列

在90.子集I中我们是通过排序,再加一个标记数组来达到去重的目的。

而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。

所以不能使用之前的去重逻辑!

Java

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTracking(nums, 0);
        return result;
    }
    private void backTracking(int[] nums, int startIndex){
        if(path.size() > 1)
            result.add(new ArrayList<>(path));            
        HashSet<Integer> hs = new HashSet<>();
        for(int i = startIndex; i < nums.length; i++){
            if(!path.isEmpty() && path.get(path.size() -1 ) > nums[i] || hs.contains(nums[i]))
                continue;
            hs.add(nums[i]);
            path.add(nums[i]);
            backTracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

与递增条件结合

整个 if 语句如下:

if (!path.isEmpty() && path.get(path.size() - 1) > nums[i] || hs.contains(nums[i]))
    continue;
  • 第一部分 !path.isEmpty() && path.get(path.size() - 1) > nums[i] 用于确保当前选择的数字 nums[i] 满足递增条件,即当前数字必须大于 path 中的最后一个数字。
  • 第二部分 hs.contains(nums[i]) 用于确保当前数字没有在同一层递归中重复选择。

当满足这两个条件中的任意一个时,都会跳过当前数字 nums[i],不将其加入 path,防止生成重复的子序列。

HashSet 的作用:

  • HashSet 用来存储在当前层递归中已经选择过的数字。如果当前数字已经在 HashSet 中存在,说明这个数字已经在当前递归层被选择过了,就跳过当前数字,避免重复选择。
  • 通过 hs.contains(nums[i]) 判断,如果当前数字已经出现过,就跳过,否则将它添加到 HashSet 中,表示已经选择过这个数字。

46.全排列

首先排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方

可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1,所以处理排列问题就不用使用startIndex了。

这里和77.组合问题131.切割问题和78.子集问题最大的不同就是for循环里不用startIndex了。

因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。

而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次

Java

class Solution {

    List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        if (nums.length == 0){
            return result;
        }
        used = new boolean[nums.length];
        backtracking(nums);
        return result;
    }

    private void backtracking(int[] nums){
        if (path.size() == nums.length){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++){
            if (used[i]){
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            backtracking(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

递归过程

  1. 遍历数组 nums 中的每个元素 nums[i]
  2. 如果该元素已经被使用(used[i]true),跳过当前元素。
  3. 如果该元素没有被使用,将其添加到 path 中,并标记为已使用。
  4. 递归调用 permuteHelper(nums),生成下一个元素。
  5. 回溯:从当前排列中移除最后一个元素,并将该元素标记为未使

47.全排列II

给定一个可包含重复数字的序列,要返回所有不重复的全排列

这里又涉及到去重了。要强调的是去重一定要对元素进行排序
image.png

Java

class Solution {
    //存放结果
    List<List<Integer>> result = new ArrayList<>();
    //暂存结果
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] used = new boolean[nums.length];
        Arrays.fill(used, false);
        Arrays.sort(nums);
        backtracking(nums, used);
        return result;
    }

    private void backtracking(int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            // used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
            // used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
            // 如果同⼀树层nums[i - 1]使⽤过则直接跳过
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            //如果同⼀树⽀nums[i]没使⽤过开始处理
            if (used[i] == false) {
                used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树枝重复使用
                path.add(nums[i]);
                backtracking(nums, used);
                path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
                used[i] = false;//回溯
            }
        }
    }
}

回溯总结

image.png
回溯算法能解决如下问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

http://www.kler.cn/a/452536.html

相关文章:

  • 学习threejs,THREE.RingGeometry 二维平面圆环几何体
  • windows和mac共享文件夹访问教程
  • 医疗平板与普通平板对比:优势尽显
  • git push origin HEAD:refs/for/分支名
  • Linux文件目录 --- touch命令创建文件
  • ReactPress 1.6.0:重塑博客体验,引领内容创新
  • Grok 2.0:马斯克的大模型挑战ChatGPT,AI竞争再升级
  • 【玩转MacBook】Maven安装
  • 大数据之 HDFS:特性与架构
  • Lua语言入门 - Lua 面向对象
  • Excel粘贴复制不完整的原因以及解决方法
  • 在git commit之前让其自动执行一次git pull命令
  • [python SQLAlchemy数据库操作入门]-05.插入数据:记录单笔股票交易信息
  • 【学习总结|DAY023】Java高级技术
  • SpringBoot统计接口请求耗时
  • Docker 部署 plumelog 最新版本 实现日志采集
  • 【前端必读】如何免费无限使用Cursor:AI编程工具的终极指南!
  • Merry Christmas HTML
  • Redis——缓存击穿
  • NetLimiter使用教程,并掌握其基本的网络管理和流量控制能力
  • 聊一聊 C#线程池 的线程动态注入 (下)
  • Flutter项目兼容鸿蒙Next系统
  • 外包干了27天,技术退步明显。。。。。
  • MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
  • UDP传输层通信协议详解
  • 33 Opencv ShiTomasi角点检测