回溯算法14:递增子序列
创始人
2025-05-29 19:25:47
0

主要是我自己刷题的一些记录过程。如果有错可以指出哦,大家一起进步。
转载代码随想录
原文链接:
代码随想录
leetcode链接: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中我们是通过排序,再加一个标记数组来达到去重的目的。

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

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

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

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

在这里插入图片描述

回溯三部曲

递归函数参数

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

代码如下:

vector> result;
vector path;
void backtracking(vector& nums, int startIndex)

终止条件

本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和回溯算法:求子集问题!

一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。

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

if (path.size() > 1) {result.push_back(path);// 注意这里不要加return,因为要取树上的所有节点
}

单层搜索逻辑
在这里插入图片描述在图中可以看出,同一父节点下的同层上使用过的元素就不能再使用了

那么单层搜索代码如下:

unordered_set uset; // 使用set来对本层元素进行去重
for (int i = startIndex; i < nums.size(); i++) {if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end()) {continue;}uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();
}

对于已经习惯写回溯的同学,看到递归函数上面的uset.insert(nums[i]);,下面却没有对应的pop之类的操作,应该很不习惯吧,哈哈

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

最后整体C++代码如下:

// 版本一
class Solution {
private:vector> result;vector path;void backtracking(vector& nums, int startIndex) {if (path.size() > 1) {result.push_back(path);// 注意这里不要加return,要取树上的节点}unordered_set uset; // 使用set对本层元素进行去重for (int i = startIndex; i < nums.size(); i++) {if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end()) {continue;}uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}
public:vector> findSubsequences(vector& nums) {result.clear();path.clear();backtracking(nums, 0);return result;}
};

以上代码用我用了unordered_set来记录本层元素是否重复使用。

其实用数组来做哈希,效率就高了很多。

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

程序运行的时候对unordered_set 频繁的insert,unordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且每次重新定义set,insert的时候其底层的符号表也要做相应的扩充,也是费事的。

那么优化后的代码如下:

// 版本二
class Solution {
private:vector> result;vector path;void backtracking(vector& nums, int startIndex) {if (path.size() > 1) {result.push_back(path);}int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]for (int i = startIndex; i < nums.size(); i++) {if ((!path.empty() && nums[i] < path.back())|| used[nums[i] + 100] == 1) {continue;}used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}
public:vector> findSubsequences(vector& nums) {result.clear();path.clear();backtracking(nums, 0);return result;}
};

这份代码在leetcode上提交,要比版本一耗时要好的多。

所以正如在哈希表:总结篇!(每逢总结必经典)中说的那样,数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如果数值范围小的话能用数组尽量用数组。

总结

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

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

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

自己的代码

class Solution {vector>result;vectorpath;void dfs(int startIndex,const vector& nums){if(path.size()>=2){result.push_back(path);}else if(path.size()>=nums.size()){return ;}unordered_set used;for(int i=startIndex;iif(used.find(nums[i])!=used.end()|| (path.size()!=0 &&nums[i]> findSubsequences(vector& nums) {dfs(0,nums);return result;}
};

相关内容

热门资讯

企业IP打造指南:小公司低成本... 小公司做企业IP,不是为了装门面,而是让客户在没见到你之前,就能通过内容知道你是谁、你解决什么问题、...
官方:赵心童入选世界斯诺克名人... 北京时间5月8日消息,世界斯诺克巡回赛(WST)今日正式公布了2025/26赛季年终奖项及名人堂更新...
小灰熊AI学员王锋:希望能跟上... 35了,老程序员了。 从进入互联网行业到现在,其实已经做了很多年移动端开发。最早那几年,安卓行业发展...
原创 2... 2026年全国两会把稳定房地产市场列为重点工作,政府工作报告明确提出因城施策控增量、去库存、优供给。...
一年翻倍,六年未归——徽商银行... 文:向善财经 今年的港股市场,与A股市场出现了明显的分化。 A股这边,科技板块在AI浪潮中热闹非凡;...
古井贡酒2025:在行业深度调... 以“稳”为底、以“新”为翼。 文/每日财报 杜康 在行业库存高企、价格倒挂的背景下,当多数酒企在为...
好上好8408万收购鼎瑞芯加码... 5月7日晚,好上好(001298.SZ)抛出一份收购公告,拟以8408万元现金收购深圳市鼎瑞芯科技有...
全面大撤离!李嘉诚英国“套现”... 突发,李嘉诚又卖了。 这次,套现了455亿。 金额不少,但更值得关注的是透露着不同寻常的信号。 因为...
油气价格上涨加剧法国一季度贸易... 据新华社,法国海关7日发布的数据显示,受中东局势推高国际油气价格影响,法国今年第一季度贸易逆差扩大至...
昆仑芯启动科创板IPO上市辅导... 5月8日,据证监会官网显示,昆仑芯(北京)科技股份有限公司于2026年5月7日正式启动科创板上市辅导...
贵州茅台酒股份有限公司关于回购... 来源:上海证券报 证券代码:600519 证券简称:贵州茅台 公告编号:临2026-016 贵州茅...
百度昆仑芯启动科创板上市辅导,... 5月8日,证监会官网显示,昆仑芯(北京)科技股份有限公司 (下称“昆仑芯”)于2026年5月7日正式...
滕州信华的承压时刻:罚单、失信... 2026年4月末,滕州信华美元债单日跌近2%,关联方被列“老赖”。半年前,这家AA+城投曾因非市场化...
002808,或被终止上市! 【导读】因触及财务类退市指标,*ST恒久或被终止上市 中国基金报记者 李智 又一A股或被终止上市。 ...
院士团队掌舵,溧阳这家企业已完... 近日,溧阳天目先导电池材料科技有限公司(下称“天目先导”)官宣完成B轮融资,投资方包括知卓创新资本、...
工商银行全新推出“工盈研选”品... 深圳商报·读创客户端记者 詹钰叶 近日,工商银行重磅推出「工盈研选」基金销售服务品牌,以客户盈利为核...
和讯信息胡云龙:逼空走势,周五... 今天市场出现逼空走势,场内投资者因持有筹码而尤为受益。五一前布局的投资者当前收获颇丰。然而,随着上证...
今晚,油价上调! 4月21日国内成品油价格下调以来,国际市场原油价格剧烈震荡,前期大幅上涨后近日有所回落,本次调价的前...
南方东英旗下两倍做多海力士,成... 【导读】南方东英旗下两倍做多海力士,成为全球最大的个股杠杆及反向产品 中国基金报记者 伊万 人工智能...
原创 金... 黄金,这东西从古至今就没离开过中国人的生活。从老辈人压箱底的小黄鱼,到如今年轻人结婚绕不开的“三金”...