代码随想录算法训练营第二十八天 | LeetCode 491. 递增子序列、46. 全排列、47. 全排列 II

news2024/11/7 11:34:29

代码随想录算法训练营第二十八天 | LeetCode 491. 递增子序列、46. 全排列、47. 全排列 II

文章链接:递增子序列        全排列        全排列II

视频链接:递增子序列        全排列        全排列II

目录

代码随想录算法训练营第二十八天 | LeetCode 491. 递增子序列、46. 全排列、47. 全排列 II

1. LeetCode 491. 递增子序列

1.1 思路

1.2 代码

2. LeetCode 46. 全排列

2.1 思路

2.2 代码

3. LeetCode 47. 全排列 II

3.1 思路

3.2 代码


1. LeetCode 491. 递增子序列

1.1 思路

  1. 这题和90. 子集 II很像,但是又有点不同,相同的是都是求子集并且是有重复元素的,并要求不能有重复组合,不同的是这题我们不可以给数组排序,因为要求的是自增子序列,自增就是要按照题目的顺序,并且元素个数是要>=2 的
  2. 而我们用回溯算法去求难免会有重复的元素,因此需要去重,而去重的逻辑又不能与40. 组合总和 II和90. 子集 II相同,因为不能排序。这题属于回溯算法和深搜都对,本质是差不多的,所有回溯都是深搜
  3. 与之前一样,还是树层去重,树枝上数值是可以取数值相同的元素的,树层上是不可以的。树枝上允许出现 [4,7,7] 这种情况,但树层上如果第二次取相同数值的元素其实在第一次取了之后往下递归的过程在树枝上已经取过第二个相同的元素了,因此要树层去重
  4. 这里收集结果的地方其实也是树上的节点,只是对结果里的元素数量有要求,要>=2 个,因此收集结果的地方依然是在回溯函数的首行,只是要加个条件,里面的元素要>=2
  5. 综上我们要去重的部分其实主要就是两部分,第一是这个树层上重复出现的元素,第二是该元素比子序列的最后一个元素小的情况
  6. 定义两个全局变量,result、path
  7. 回溯函数的参数和返回值:返回值 void,参数 nums,startIndex 控制剩余集合的起始位置
  8. 终止条件:startIndex>=nums.length
  9. 单层搜索的逻辑:进入递归就收集结果,因为每个节点都是我们的结果,但是本题需要判断一下,与元素个数要>=2,把 path 加入到 result 中
  10. 要先定义个 HashSet set,就是做树层去重的,我们怎么知道同层前面取过了某个元素接下来就不能取了呢?就用 set 记录一下,for(int i=startIndex;i<nums.length;i++),for循环每取一个就放入到 set 里,然后 for循环里判断如果要取的这个数已经在 set 里了就跳过此次循环。然后我们在取数时如果取的数比子序列最后一个元素小的话就不取,即 if( !path.isEmpty() && nums[i]<path.get(path.size()-1 || set.contains(nums[i]) )continue,这里的意思就是如果取的这个数比自序列最后一个元素小或者同一层已经取过这个数了就跳过,这里要加 !path.isEmpty() 因为集合为空会报异常。这里为什么是 continue 是因为这个元素不可以取了,下一个可能是能取的。
  11. 然后就是取数的过程,set.add(nums[i]),path.add(nums[i])。然后是递归 backtracking(nums, i+1),然后是回溯的过程 path.remove(path.size()-1)。这里为什么不需要把 set 也回溯呢?因为这个 set 只是记录同一层里是否取过某个数,而进入递归时是新创建一个 set,再重新记录同一层是否取过某个数,set 的作用就是记录当前这层递归的元素别取重复了

1.2 代码

//
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() >= 2)
                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);
        }
    }
}

2. LeetCode 46. 全排列

2.1 思路

  1. 这题没有重复元素,因此不用考虑去重。这题是排列,需要清除与组合的区别。
  2. 这题需要用个 used 数组,之前都是用 used 来去重的,这里不需要去重为什么也用呢?这里是用 used 数组是标记哪个元素使用过了,我们求排列相同元素不能重复使用,和之前的去重逻辑是不一样的。做组合类问题时都是用 startIndex 来避免重复取同一个元素和出现 [1,2][2,1] 这种情况。
  3. 这题的结果都是在叶子节点上的,树的深度就是由集合的大小长度来控制的,因为最后取的排列就是和集合一样大的
  4. 定义三个全局变量,result、path、used 数组标记哪个元素已经使用过
  5. 回溯函数的参数和返回值:返回值 void,参数 nums
  6. 终止条件:当 nums.length==path.size()就把 path 加入到 result 中然后 return
  7. 单层搜索的逻辑:for(int i=0; i<nums.length; i++)这里为什么从 0 开始?因为之前用 startIndex 控制是为了避免重复,比如出现[1,2][2,1] 这种情况,而现在求排列 [1,2][2,1] 都是我们要的结果,因此每次取数都要从头开始,唯一要跳过的情况就是这一层取过这个数了,下一层就别取这个数了,也就是用 used 数组控制的。if(used[i]==true)就 continue,因为是 true 说明上一层用过了,就跳过此次循环。然后就是取数的过程,used[i]=true ,path.add(nums[i])。然后就是下一层递归了,backtracking(nums)。然后就是回溯,就是 used[i]=false,path.removeLast()

2.2 代码

//
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];
        permuteHelper(nums);
        return result;
    }

    private void permuteHelper(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]);
            permuteHelper(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

3. LeetCode 47. 全排列 II

3.1 思路

  1. 这题和46. 全排列的区别就是这题有重复元素,这样就会导致出现相同的排列不如两个{1,1,2},因此要去重(注意这里去重需要先对题目的数组排序,因为要让相同的元素挨在一起)。同时这里也需要 used 数组标记哪些元素是否使用过,避免出现了使用两次同一个元素
  2. 依然是从树层去重和树枝去重的角度看。同一树层上,前面取过 1 后面就不能取 1 了,因为剩余集合是相同的,再取一次就会得到相同的排列,就重复了。而在同一树枝上上面取过 1 了,下面还可以取 1,这里没有重复取 1,上面取的是第一个 1 下面取的是第二个 1,这里是两个元素,排列里可以出现比如{1,1,2}这样子可以出现两个数值相同的元素。而结果是在叶子节点上,我们要砍去的分支都是在树层上如果和前面的树枝相同就要砍掉
  3. 定义三个全局变量,result、path、used 数组标记哪个元素已经使用过
  4. 回溯参数的参数和返回值:返回值 void,参数 nums
  5. 终止条件:当 path.size()==nums.length 就把 path 加入到 result 中然后 return
  6. 单层搜索的逻辑:for(int i=0; i<nums.length; i++)这里为什么从 0 开始?因为之前用 startIndex 控制是为了避免重复,比如出现[1,2][2,1] 这种情况,而现在求排列 [1,2][2,1] 都是我们要的结果,因此每次取数都要从头开始。而取数时有些不能取的,(i>0&&nums[i-1]==nums[i]&&used[i-1]==false)i>0 避免数组越界,然后树层上遇到相同元素就砍掉后面的分支,如果知道是树层而不是树枝呢?used[i-1]=false 就是树层,true 就是树枝,因为是回溯过来往树层右边取的。如果这个括号的条件为 true 就 continue 跳过此次循环,就是继续往树层右边搜索
  7. 然后是挨个取数的逻辑,取数时 if(used[i]==true)就 continue,这个情况就是排列里不能取同一个数的逻辑。然后就继续取数,used[i]=true,path.add(nums[i]),然后递归函数,然后回溯 used[i]=false,path.removeLast()

3.2 代码

//
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);
        backTrack(nums, used);
        return result;
    }

    private void backTrack(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]);
                backTrack(nums, used);
                path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
                used[i] = false;//回溯
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1114897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用VGG框架实现从二分类到多分类

一.数据集的准备 与之前的不同&#xff0c;这一次我们不使用开源数据集&#xff0c;而是自己来制作数据集。重点需要解决的问题是对数据进行预处理&#xff0c;如每一个图片的大小均不同&#xff0c;需要进行resize&#xff0c;还需要对每一张图片打标签等操作。 数据集文件 …

根据pid查看jar包(windows)

打开jdk/bin/jvisualvm.exe,根据pid找到jar包的主启动类,jdk14以后不再默认使用,官网下载,也可以使用老版本的查看

虚拟机如何联网【NAT】

查看VMWARE的IP地址 #进入root用户 su -#更改虚拟网卡设置界面 vi /etc/sysconfig/network-scripts/ifcfg-ens33 修改ONBOOT为yes BOOTPROTO为static IPADDR为前面的网段 192.168.211.xx (xx为自己设置的&#xff0c;可以随意设置&#xff0c;前面的为前面查看的IP地址的前…

黑客技术(自学方法)——网络安全

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学&#xff1f;如何学&#xff1f; 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c…

jupternotebook和jupterLab有什么区别?

目录 1.jupternotebook 2.jupterLab 3.总结 Jupyter Notebook和JupyterLab是两个常用的交互式计算环境&#xff0c;都是基于Jupyter项目开发的。它们具有一些共同的特性&#xff0c;但也存在一些区别。 1.jupternotebook Jupyter Notebook是Jupyter项目的早期版本&#xff…

【数据结构】线性表的顺序存储结构

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 一.顺序存储定义 上篇文章中介绍了线性表一共分为两种数据结构——顺序存储结构和链式存储结构. 今天我们就来一起学习一下第一种——顺序存储结构. 线性表的顺序存储结构,指…

AUTOSAR AP硬核知识点梳理(1)

一 什么是 Adaptive AUTOSAR? Adaptive AUTOSAR是一种新的汽车软件框架,旨在满足现代汽车行业中不断增长的技术需求。随着汽车变得越来越智能,对处理器的性能要求也在不断增长。 Adaptive AUTOSAR旨在通过提供高性能计算和通信机制以及灵活的软件配置来满足这些需求,为车…

代码随想录算法训练营第五十三天 | 309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 视频讲解&#xff1a; https://programmercarl.com/0309.%E6%9C%80%E4%BD%B3%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E6%97%B6%E6%9C%BA%E5%90%AB%E5%86%B7%E5%86%BB%E6%9C%9F.html &#xff08;1&#xff09;代码 714.买卖股票的最佳时机含手续费…

通过字符设备驱动并编写应用程序控制三盏灯亮灭

现象 键盘按1三灯全亮 按0三灯全灭 头文件.h #ifndef __HEAD_H__ #define __HEAD_H__ #define PHY_LED1_MODER 0X50006000 #define PHY_LED1_ODR 0X50006014 #define PHY_RCC 0X50000A28#define PHY_LED2_MODER 0X50007000 #define PHY_LED2_ODR 0X50007014#defin…

底层驱动day2作业

控制三盏灯亮灭 代码&#xff1a; //head.h#ifndef __HEAD_H__ #define __HEAD_H__ #define PHY_RCC 0x50000A28 #define PHY_GPIOE_MODER 0x50006000 #define PHY_GPIOF_MODER 0x50007000 #define PHY_GPIOE_ODR 0x50006014 #define PHY_GPIOF_ODR 0x50007014#endif //demo…

JAVA毕业设计100—基于Java+Springboot+Vue的WMS仓库管理系统+移动端微信小程序(源码+数据库+部署视频)

基于JavaSpringbootVue的WMS仓库管理系统移动端(源码数据库部署视频) 一、系统介绍 本系统前后端分离带小程序 本系统分为管理员、用户角色(角色权限可自行分配) 功能列表&#xff1a; 1、 数据管理&#xff1a;物料数据管理、物料Bom管理、物料组管理、物料分类管理、供应…

口袋参谋:如何提升宝贝的点击率?这两种方法超简单!

​如何提升宝贝的点击率&#xff1f;这是99.99%商家都在疑惑的问题。今天我说的两种方法&#xff0c;超简单&#xff0c;一起来看看吧&#xff01; 1、找精准关键词 首先一定要选择适合自己店铺的关键词&#xff0c;一定要精准。 找关键词的方法如下&#xff1a; a.卖家可以…

Coreldraw2020最新64位电脑完整版本下载教程

安装之前所有的杀毒软件都要退出。无论是360&#xff0c;腾讯管家&#xff0c;或者电脑自带的安全中心&#xff0c;要不然会阻止安装。 CorelDRAW2020版win下载如下:https://wm.makeding.com/iclk/?zoneid55678 CorelDRAW2020版mac下载如下:https://wm.makeding.com/iclk/?…

2023年全球及中国溶瘤病毒治疗药物行业现状及发展趋势分析[图]

溶瘤病毒治疗肿瘤具有杀伤效率高、靶向性好、安全性高、不良反应小等特点&#xff0c;溶瘤病毒疗法已经成为肿瘤治疗研究领域的新热点。溶瘤病毒疗法经历了早期对于天然病毒的摸索&#xff0c;后期基因工程技术的进步提升OV靶向性及免疫致敏能力&#xff0c;推动其快速发展。 …

性能测试 —— 数据准备与基准场景设计!

基础性能脚本 延迟时间网络时间服务处理时间 主要使用监听器&#xff0c;通过两者的差值&#xff0c;判断响应时间消耗&#xff1a; jpgc - Connect Times Over Timejpgc - Response Times Over Time 调试脚本阶段&#xff0c;可以使用查看结果树等监听器来观察运行结果 正…

经典网络模型

Alexnet VGG VGG的启示 VGGNet采用了多次堆叠3x3的卷积核&#xff0c;这样做的目的是减少参数的数量。 例如&#xff0c;2个3x3的卷积核效果相当于1个5x5的卷积核效果&#xff0c;因为它们的感受野&#xff08;输入图像上映射区域的大小&#xff09;相同。但2个3x3卷积核的参数…

使用SpringCloudalibaba+Vue开发仿社交小程序全套视频课程

使用SpringCloudalibabaVue开发仿社交小程序全套视频课程 学习此课程你将会学到&#xff1a; 1. 熟练掌握小程序开发与部署2. 学会前后端分离开发与联调3. 从0到1学会微服务架构与落地4. 掌握主流中间件的封装与设计5. 掌握复杂数据库分库分表6. 收获一个商业级的面试作品 适…

MyBatisPlus实现连表操作、批量处理

1、实现连表查询 正常来说单靠mybatisplus无法实现连表查询&#xff0c;只能靠单表sql然后进行拼接形成连表查询&#xff0c;或者使用xml文件去编写sql语句来实现连表查询。但他又给我们提供了一个插件MyBatis-Plus-Join&#xff0c;用来弥补mybatisplus再连表上的不足&#…

中间件安全-CVE复现IISApacheTomcatNginx漏洞复现

目录 中间件安全&CVE复现&IIS&Apache&Tomcat&Nginx漏洞复现中间件-IIS安全问题中间件-Nginx安全问题漏洞复现Nginx 解析漏洞复现Nginx 文件名逻辑漏洞 中间件-Apache-RCE&目录遍历&文件解析等安全问题漏洞复现漏洞复现CVE_2021_42013 RCE代码执行&…

LeetCode-496 下一个更大元素

一、前言 今天想要分享的题目其实和之前写的股票价格那道题所用到的结构是一样的&#xff0c;就是单调栈&#xff0c;因为自己当时也对这种结构不太熟悉&#xff0c;看到评论区很多大佬说如果遇到要找下一个最大/最小这一类的题目&#xff0c;可以往单调栈这一块儿去想&#x…