491. 递增子序列

文章目录

  • 491. 递增子序列
  • 思路
  • 回溯三部曲
  • 总结

491. 递增子序列

491. 递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]

提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

思路

这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。

这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的90.子集II

就是因为太像了,更要注意差别所在,要不就掉坑里了!

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

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

所以不能使用之前的排序+used切片标记同层相同元素是否使用过的方式处理去重逻辑!!!

本题给出的示例,还是一个有序数组 [4, 6, 7, 7],这更容易误导大家按照排序的思路去做了。

为了有鲜明的对比,我用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:

在这里插入图片描述

回溯三部曲

1.递归函数参数
本题求子序列,很明显所给列表中一个元素不能重复使用,所以需要startIndex,用于调整下一层递归的起始位置。

代码如下:

func backtracking(nums []int,res *[][]int,path *[]int,startIndex int) {}

2.终止条件
本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和回溯算法:求子集问题!一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。

但本题收集结果有所不同,题目要求递增子序列大小至少为2,所以代码如下:

if len(*path) >= 2 {*res = append(*res,append([]int(nil),*path...))   // 注意这里不要加return,因为要取树上的所有节点,所以还要继续往下取}

3.单层搜索逻辑
在这里插入图片描述

从图中可以看出,同一父节点下的同层上使用过的元素就不能再使用了

那么单层搜索代码如下:

// 每一层都有自己的一个set,使用set来对本层元素进行去重set := make(map[int]bool)for i := startIndex;i < len(nums);i++ {// 当前数字在本层已经用过了,不要再用了,故continue,去重if  set[nums[i]] {continue}if len(*path) > 0 && nums[i] < (*path)[len(*path) - 1] {continue}set[nums[i]] = true // 记录这个元素在本层用过了,本层后面不能再用了*path = append(*path,nums[i])backtracking(nums,res,path,i + 1)*path = (*path)[0:len(*path) - 1]}

对于已经习惯写回溯的同学,看到递归函数上面的set[nums[i]] = true,下面却没有对应的set[nums[i]] = false回溯之类的操作,应该很不习惯吧

这也是需要注意的点,set是记录本层元素是否重复使用,新的一层set都会重新定义,所以要知道set只负责本层!

深入理解对比一下:

排序后 + used切片标记去重的代码,因为切片是排序过的,同层当前要选的数字和之前的相同,但是前面的那个数字还没有标记过,说明是同层重复使用某个数字了,要去重,且used维护的是全局某个数字是否用过,不是特定某层的,所以used切片需要回溯

for i := startIndex;i < len(candidates) && target - candidates[i] >= 0;i++ {if i > 0 && candidates[i] == candidates[i - 1]  && !used[i - 1]{// 同层横向遍历,前一个相同数字没有用过就用后一个数字,是重复的,要去重continue}*path = append(*path,candidates[i])used[i] = truebacktracking(candidates,target - candidates[i],res,path,i+1,used)*path = (*path)[0:len(*path) - 1]used[i] = false
}

用set去重的代码,因为每层都是各自维护同层之前某个相同的数字是否使用过,用过就表示当前同层再选这个元素就重复使用了,要去重,且set不用回溯,因为它本身就是想标记该层某个元素是否用过了。

// 每一层都有自己的一个set,使用set来对本层元素进行去重
set := make(map[int]bool)
for i := startIndex;i < len(nums);i++ {// 当前数字在本层已经用过了,不要再用了,故continue,去重if  set[nums[i]] {continue}if len(*path) > 0 && nums[i] < (*path)[len(*path) - 1] {continue}set[nums[i]] = true // 记录这个元素在本层用过了,本层后面不能再用了*path = append(*path,nums[i])backtracking(nums,res,path,i + 1)*path = (*path)[0:len(*path) - 1]
}

最后整体Go代码如下:

func findSubsequences(nums []int) [][]int {// 选择下一个元素时,需要判断是否大于已选路径中最后添加的元素(它最大)// 由于每个子集不能改变在原数组中的相对顺序,所以没法用排序+used切片辅助去重(因为used需要用到i-1下标)// 因此我们用下Set记录同层某个元素是否已经用过,每层都维护各自的Setif len(nums) == 0 {return nil}res := make([][]int,0)path := make([]int,0)backtracking(nums,&res,&path,0)return res
}func backtracking(nums []int,res *[][]int,path *[]int,startIndex int) {if len(*path) >= 2 {*res = append(*res,append([]int(nil),*path...))  // 注意这里不要加return,因为要取树上的所有节点,所以还要继续往下取 }// 每一层都有自己的一个set,使用set来对本层元素进行去重set := make(map[int]bool)for i := startIndex;i < len(nums);i++ {// 当前数字在本层已经用过了,不要再用了,故continue,去重if  set[nums[i]] {continue}if len(*path) > 0 && nums[i] < (*path)[len(*path) - 1] {continue}set[nums[i]] = true // 记录这个元素在本层用过了,本层后面不能再用了*path = append(*path,nums[i])backtracking(nums,res,path,i + 1)*path = (*path)[0:len(*path) - 1]}
}

在这里插入图片描述

总结

本题题解清一色都说是深度优先搜索,但我更倾向于说它用回溯法,而且本题我也是完全使用回溯法的逻辑来分析的。

相信大家在本题中处处都能看到是回溯算法:求90.子集II 的身影,但处处又都是陷阱。

对于养成思维定式或者套模板套嗨了的同学,这道题起到了很好的警醒作用。更重要的是拓展了大家的思路!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中的进程间通信之管道

管道 管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道” 匿名管道 #include <unistd.h> 功能:创建一无名管道 原型 int pipe(int fd[2]); 参数 fd&#xff1a;文件描述符数组,其中fd[0]表示读端, fd[1]表示写端 …

解锁PDF阅读器的神奇功能与应用场景

PDF格式的文档因其稳定性、兼容性和安全性&#xff0c;成为了广泛传播和存储信息的重要载体。而PDF阅读器则是我们打开这个数字知识宝库的关键钥匙。接下来&#xff0c;让我们一同走进福昕PDF阅读器和它小伙伴们的世界&#xff0c;去探索它们的神奇之处。 1.福昕阅读器 链接一…

Spring Boot 和 MyBatis-Plus凑一块儿了,这份教程你得看

一、引言 MyBatis-Plus 是 MyBatis 的增强版&#xff0c;提供了 CRUD 接口、分页插件、性能分析插件等特性&#xff0c;简化了开发过程。本文将详细介绍如何在 Spring Boot 项目中集成 MyBatis-Plus。 支持的数据看也越来越多&#xff0c;值得去搞一下&#xff0c;写了一个小例…

Hive数仓操作(八)

一、Hive中的分桶表 1. 分桶表的概念 分桶表是Hive中一种用于提升查询效率的表类型。分桶指的是根据指定列的哈希值将数据划分到不同的文件&#xff08;桶&#xff09;中。 2. 分桶表的原理 哈希分桶&#xff1a;根据分桶列计算哈希值&#xff0c;对哈希值取模&#xff0c;将…

三维激光扫描技术在文保修缮项目中的应用

三维激光扫描技术作为一种新兴的高精度空间数据获取手段&#xff0c;其在文物保护和修缮项目中的应用日益广泛。这项技术通过快速获取物体表面的三维密集点云数据&#xff0c;为文物的数字化存档、保护、修复及再利用提供了强有力的技术支持。 数据采集&#xff1a;高精度与非接…

Python案例--水仙花数的探索之旅

一、引言 水仙花数&#xff0c;也称为阿姆斯特朗数&#xff0c;是一种特殊的三位数&#xff0c;其各位数字的立方和等于其本身。例如&#xff0c;153就是一个水仙花数&#xff0c;因为 135333153135333153。这种数字的发现不仅展示了数字的内在美&#xff0c;也激发了人们对数…

Element-plus安装及其基础组件使用

简而言之&#xff0c;在main.js中导出以下库,仅此&#xff0c;搞多了出错难排查 import ElementPlus from element-plus //导入ElementPlus 模块 import element-plus/dist/index.css //引入样式 app.use(ElementPlus) //注册库就能使用了 Element Plus 是一个基于 Vue 3 的组件…

《Linux从小白到高手》理论篇(十一):Linux的系统环境管理

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。本篇详细深入介绍Linux的系统环境管理。 环境变量 linux系统下&#xff0c;如果你下载并安装了应用程序&#xff0c;很有可能在键入它的名称时出现“command not found”的提示内容。如果每…

2024必备英语在线翻译工具推荐

英语在线翻译工具就如同一位随时待命的语言助手&#xff0c;为我们打破语言障碍&#xff0c;搭建起沟通的桥梁。接下来&#xff0c;让我们一起深入了解这些英语在线翻译工具的丰富功能及其为我们带来的便利。 1.福昕在线翻译 链接直达>>https://fanyi.pdf365.cn/doc …

命令按钮QLink

主要作用用来点击后可以自动打开系统的网页浏览器&#xff0c;跳转到指定的网页 常用方法 文本 //获取和设置文本 QString text() const void setText(const QString &text)描述信息 //获取和设置描述文本 QString description() const void setDescription(const QSt…

【RabbitMQ】面试题

在本篇文章中&#xff0c;主要是介绍RabbitMQ一些常见的面试题。对于前几篇文章的代码&#xff0c;都已经在码云中给出&#xff0c;链接是mq-test: 学习RabbitMQ的一些简单案例 (gitee.com)&#xff0c;如果存在问题的话欢迎各位提出&#xff0c;望共同进步。 MQ的作用以及应用…

【web安全】——XXE漏洞

1.XML基础 1.1.XML简介 XML被称为可扩展标记语言&#xff0c;与HTML类似&#xff0c;但是HTML中的标签都是预定义(预先定义好每个标签的作用)的&#xff0c;而XML语言中的标签都是自定义(可以自己定义标签的名称、属性、值、作用)的;HTML中的标签可以是单标签&#xff0c;而X…

SpringMVC源码-SpringMVC框架中Spring父容器和SpringMVC子容器加载的流程以及SpringMVC九大内置组件的初始

一、Spring父容器启动 SpringMVC 的项目结构如下: applicationContext.xml spring的配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.o…

机器学习西瓜书笔记(十三) 第十三章半监督学习+代码

第十三章 13 半监督学习13.1 未标记样本13.3.1 小结 13.2 生成式方法13.2.1 小结 13.3 半监督SVM13.3.1 小结 13.4 图半监督学习13.4.1 小结 13.5 基于分歧的方法13.5.1 小结 13.6 半监督聚类13.6.1 小结 13.7 代码&#xff1a;手写数据集上的标签传播-性能展示章末小结 13 半监…

数据结构——初识树和二叉树

线性结构是一对一的关系&#xff0c;意思就是只有唯一的前驱和唯一的后继&#xff1b; 非线性结构&#xff0c;如树形结构&#xff0c;它可以有多个后继&#xff0c;但只有一个前驱&#xff1b;图形结构&#xff0c;它可以有多个前驱&#xff0c;也可以有多个后继。 树的定义…

变电站红外检测数据集 1180张 变电站红外 标注voc yolo 13类

变电站红外检测数据集 1180张 变电站红外 标注voc yolo 13类 变电站红外检测数据集 名称 变电站红外检测数据集 (Substation Infrared Detection Dataset) 规模 图像数量&#xff1a;1185张图像。类别&#xff1a;13种设备类型。标注个数&#xff1a;2813个标注。 数据划分…

多模态RAG实现

在标准 RAG 中&#xff0c;输入文档包含文本数据。LLM 利用上下文学习&#xff0c;通过检索与所提查询上下文相匹配的文本文档块来提供更相关、更准确的答案。 但是&#xff0c;如果文档包含图像、表格、图表等以及文本数据&#xff0c;该怎么办&#xff1f; 不同的文档格式包…

华为GaussDB数据库之Yukon安装与使用

一、Yukon简介 Yukon&#xff08;禹贡&#xff09;&#xff0c;基于openGauss、PostgreSQL、GaussDB数据库扩展地理空间数据的存储和管理能力&#xff0c;提供专业的GIS&#xff08;Geographic Information System&#xff09;功能&#xff0c;赋能传统关系型数据库。 Yukon 支…

linux桌面软件(wps)内嵌到其他窗口

程序测试环境是&#xff1a;slackware系统&#xff0c;属于linux系统&#xff0c;有桌面&#xff08;Xface Session&#xff09;。系统镜像是&#xff1a;slackware64-15.0-install-dvd.iso。qt、c代码实现。 程序功能&#xff1a;将已经打开的wps&#xff08;word、pdf等都可…

Android SystemUI组件(09)唤醒亮屏 锁屏处理流程

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分 唤醒亮屏 即可。 Power按键的处理逻辑最终是由PhoneWindowManager来…