代码随想录算法训练营第二十九天 | 递增子序列(新的树层去重)、排列、排列中树枝树层去重

news2024/9/28 7:25:33

491.递增子序列

文档讲解:代码随想录 (programmercarl.com)

视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili

状态:能直接写出来。不过还是要再看一遍,因为是新的去重类型以及递增类型。

思路

画了示例1的树形图,可以看出,用的是子集的方法:每个节点都要存入结果集。另外,还要进行树层去重,如图中绿色圈。由于该题不能对原数组排序,故只能用set

在这里插入图片描述

画了示例2的树形图,可以看出,当前层选取的结点值必须小于上一层的节点值,才能出现递增,如下图红色圈。而上一层的节点值就是当前path数组的最后一个元素

在这里插入图片描述

整体代码

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums, int startIndex){
        if(path.size() >= 2){	//递增子序列中 至少有两个元素 
            result.push_back(path);
        }

        unordered_set<int> uset;//使用set来对本层元素进行去重,新的一层uset都会重新定义(清空),所以要知道uset只负责本层
        for(int i = startIndex; i < nums.size(); ++i){
            if(uset.find(nums[i]) != uset.end()) continue;  //!树层去重
            if(!path.empty() && nums[i] < path.back()) continue; //!当前元素小于上一层元素,则不递增,跳过

            uset.insert(nums[i]);// 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            ++i;
            backtracking(nums, i);
            --i;
            path.pop_back();
        }
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backtracking(nums, 0);
        return result;
    }
};

优化

用数组来实现哈希。题目中说了,数值范围[-100,100],所以完全可以用数组来做哈希。

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums, int startIndex){
        if(path.size() >= 2){
            result.push_back(path);
        }

        int used[201] = {0}; // 使用数组来对本层元素进行去重,新的一层数组都会重新定义(清空),所以要知道数组只负责本层!
        for(int i = startIndex; i < nums.size(); ++i){
            if(used[nums[i] + 100] == 1) continue;  //!树层去重
            if(!path.empty() && nums[i] < path.back()) continue; //!当前元素小于上一层元素,则不递增,跳过

            used[nums[i] + 100] = 1;// 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            ++i;
            backtracking(nums, i);
            --i;
            path.pop_back();
        }
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backtracking(nums, 0);
        return result;
    }
};

46.全排列

文档讲解:代码随想录 (programmercarl.com)

视频讲解:组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列_哔哩哔哩_bilibili

状态:能直接做出来。

思路

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

  • 由于排列是有序的,所以每次for循环都要从头开始,因此不用startIndex。
  • 排列中每个元素只能被用一次,故用used数组表示某个元素是否被用过了,即树枝去重。而组合问题有startIndex,已达到树枝去重的效果。
  • 当收集元素的数组path的大小和nums数组一样大时,说明找到一个全排列,即表示到达了叶子节点。

在这里插入图片描述

我写的代码

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    int used[21] = {0};
    void backtracking(vector<int>& nums){
        if(path.size() == nums.size()){ // 此时说明找到了一组
            result.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); ++i){
            if(used[nums[i] + 10] == 1) continue;   // path里已经收录的元素,直接跳过

            used[nums[i] + 10] = 1;
            path.push_back(nums[i]);
            backtracking(nums);
            path.pop_back();
            used[nums[i] + 10] = 0;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        backtracking(nums);
        return result;
    }
};

文档的代码:只是把全局used变成函数的一个形参而已。

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

总结

排列问题的不同:

  • 每层都是从0开始搜索而不是startIndex
  • 需要used数组记录path里都放了哪些元素了

47.全排列 II

文档讲解:代码随想录 (programmercarl.com)

视频讲解:回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II_哔哩哔哩_bilibili

状态:能写出来。

思路

题目数组中有重复元素而要求结果中的各个集合不重复,所以用树层去重。由于可以重新排序,所以用数组进行树层去重

由于**“数组进行树层去重”的数组used[i - 1] == false表示树层重复,used[i - 1] == true表示树枝重复**,所以不用再像上一题一样“开辟一个数组用于限制每个元素只能使用一次”,因为这是树枝去重,可以利用used来判断树枝重复,从而避免多开辟一个数组造成冗余。

我写的代码

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums, vector<bool>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); ++i){
            if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue;   //树层去重

            if(used[i] == false){   //树枝去重
                used[i] = true;
                path.push_back(nums[i]);
                backtracking(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果

拓展

去重最为关键的代码为:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue;

如果改成 used[i - 1] == true, 也是正确的!,去重代码如下:

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) continue;

这是为什么呢,就是上面我刚说的,如果要对树层中前一位去重,就用used[i - 1] == false,如果要对树枝前一位去重用used[i - 1] == true

对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!

下面是[1, 1, 1]举例:

树层上去重(used[i - 1] == false),的树形结构如下:

在这里插入图片描述

树枝上去重(used[i - 1] == true)的树型结构如下:

在这里插入图片描述

可以很清晰的看到,树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索。

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

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

相关文章

行人重识别(REID)——原理方法

行人重识别&#xff1a;短时 类内差异增大&#xff0c;类间差异减小 应用——行人跟踪 单摄像头单目标单摄像头多目标多摄像头多目标 行人重识别系统 特征提取 学习能够应对在不同摄像头下行人变化的特征 度量学习 将学习到的特征映射到新的空间使相同的人更近&#xff0c…

【每日一练】谷歌面试题:用JAVA翻转二叉搜索树

文章目录 前言题目分析实战演示1、创建一颗搜索二叉树2、中序遍历二叉搜索树3、根据题意创建二叉搜索树并展示4、算法增加二叉树翻转方法5、根据题意测试翻转二叉树结果6、完整代码 前言 很多同学应该都能够模拟出一个二叉树&#xff0c;那么又有多少同学能够写出翻转二叉树呢…

2d俯视视角游戏,可以切换多种枪械

文章目录 一、 介绍二、 人物移动、鼠标控制转向三、子弹脚本四、子弹随机抛壳五、 爆炸特效六、 发射子弹七、 子弹、弹壳对象池八、 散弹枪九、 火箭弹、发射火箭十、 下载工程文件 一、 介绍 2d俯视视角游戏。 人物视角跟随鼠标移动 多种枪械 抛壳效果 多种设计效果 对象池…

『python爬虫』10. 数据解析之xpath解析(保姆级图文)

目录 安装库xpath入门怎么快速得到xpath路径xpath节点的关系xpath方法小型实战总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 安装库 pip install lxmlxpath入门 怎么快速得到xpath路径 &#xff08;相对路…

第三十章 Unity角色控制器 Character Controller

在我们之前的章节中&#xff0c;我们已经了解了碰撞体和刚体。但是&#xff0c;对于刚体这个组件来讲&#xff0c;有两种使用方式。其一就是用它来模拟现实世界的移动或碰撞效果&#xff08;例如&#xff0c;门的开关&#xff09;&#xff1b;其二就是使用代码来控制物体移动或…

【网络进阶】HTTP服务器(一)

文章目录 1. HTTP简介2. HTTP工作原理3. HTTP注意事项4. HTTP消息结构5. 客户端请求消息6. 服务器响应消息7. GET传递数据实例8. HTTP请求方法9. HTTP响应头信息 1. HTTP简介 HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于传…

Spring Cloud的五大组件你知道多少

前言 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用Spring Boot的开发风格做到一键启动和部署。 Spring Clo…

瑞萨e2studio(25)----电容触摸配置(2)

瑞萨e2studio.24--电容触摸配置1 概述初始配置监控检测电容按键显示和测量标准差显示多个触摸按键曲线 概述 篇文档将在上篇文章基础上修改电容触摸配置。 初始配置 需要进入Debug模式才可以进行电容触摸配置。 监控检测电容按键 从电容触摸主界面&#xff08;QE&#xf…

解决Xshell安装时错误代码-1603的问题

安装流程 官网下载&#xff1a;家庭/学校免费 - NetSarang Website 填写姓名&#xff0c;邮箱&#xff0c;申请下载&#xff0c;就能在邮箱收到下载链接 点击链接即可自动开始下载&#xff1b; 下载完成后进行安装即可 问题描述 安装进行到最后一步时&#xff0c;出现下图…

CCED2000后,中文编程软件再次脱颖而出,系出金山

WPS抗衡微软&#xff0c;CCEDE却被淹没&#xff1f; DOS代&#xff0c;我们用WPS来进行文字编辑&#xff0c;CCED来做表格&#xff0c;两者在那个时代可以称得上是国产办公领域的“必装软件”。 如今&#xff0c;30年过去了&#xff0c;WPS一步一步成长为抗衡微软office的国产…

4d毫米波雷达聚类检测和追踪

待整理和写代码&#xff0c;准备先用dbcan聚类&#xff0c;用激光那一套做做看看效果 流程 4D雷达毫米波聚类跟踪流程如下图&#xff1a; 预处理主要包括标定、坐标转换和动静分离。 标定使用水平仪、角反&#xff0c;采集数据分析&#xff0c;得到水平和俯仰偏角。 坐标转…

nssctf web (3)

[HUBUCTF 2022 新生赛]checkin <?php show_source(__FILE__); #将当前文件的代码显示到页面 $username "this_is_secret"; #给username赋值 $password "this_is_not_known_to_you"; #给password赋值 include("flag.php");//here I ch…

MySQL数据管理

一、MySQL数据库管理 1、库和表 行&#xff08;记录&#xff09;&#xff1a;用来描述一个对象的信息 列&#xff08;字段&#xff09;&#xff1a;用来描述对象的一个属性 2、常用的数据类型 int &#xff1a;整型 float &#xff1a;单精度浮点 4字节32位 double &…

《网络安全审查办法》

1发展历程 2020年4月27日&#xff0c;12部门联合发布《网络安全审查办法》&#xff0c;2020年6月1日起实施。 2021年7月10日&#xff0c;国家互联网信息办公室发布关于《网络安全审查办法&#xff08;修订草案征求意见稿&#xff09;》公开征求意见的通知。11月16日国家互联网信…

【C++入门】你知道为什么C++有函数重载而C语言没有函数重载吗?

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

《最强Android书 架构大剖析》读书笔记

文章目录 第一章 Android 体系结构的变革之路1.2 Android系统源码目录与Linux的异同Android的框架原生二进制可执行文件Android 的原生库核心(core)库用以支持框架的库硬件抽象层Linux内核不带上层 UI界面的Android 第二章 Android 的分区和文件系统2.1 分区架构实验:从设备中获…

ffmpeg学习日记513-源码-configure_filtergraph()函数分析及功能

Date: 12/04/2023 Hours: Details:template_tags 文章目录 源码版本实现文件函数原型参数释义函数功能函数分析filtergraph_is_simple函数 总结参考 源码版本 ffmpeg-4.1.10 实现文件 fftools/ffmpeg_filter.c 函数原型 int configure_filtergraph(FilterGraph *fg)参数释…

重识三只松鼠:“二次创业”的新变革,“深蹲起跳”的新动能

“羚羊明白它必须跑得比狮子快&#xff0c;不然它会被狮子吃掉&#xff1b;每天早晨狮子醒来&#xff0c;狮子也明白它必须赛过跑得最慢的羚羊&#xff0c;不然它会活活饿死。不论你是狮子还是羚羊&#xff0c;都不重要……重要的是每天旭日东升&#xff0c;你就得开始奔跑&…

突破经典网格特征?AutoFocusFormer: Image Segmentation off the Grid 论文阅读笔记

突破经典网格特征&#xff1f;AutoFocusFormer: Image Segmentation off the Grid 论文阅读笔记 一、Abstract二、引言三、相关工作视觉 Transformer Backbones基于聚类的注意力自适应下采样点云网络 四、方法4.1 聚类和区域4.1.1 平衡聚类4.1.2 聚类的区域 写在前面 这一周赶上…

【五一创作】Visual Studio常用调试技巧的温习

当你在编写C程序时&#xff0c;难免会遇到代码出现错误的情况。这时候就需要用调试工具来定位问题并解决它。以下是一些在Visual Studio中使用调试器时常用的技巧&#xff0c;权当作温故总结罢~ 1. 断点 断点是调试中最为基本但也最常用的技巧之一。通过在需要定位的代码行上打…