491.递增子序列

卡哥建议:本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。

 https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html

视频讲解:https://www.bilibili.com/video/BV1EG4y1h78v

做题思路:

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

而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用之前的去重逻辑!

用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:

还得有树层去重,如上图的同一父节点不能重复使用7,同一父节点下的同层上使用过的元素就不能再使用了 

 关于uset,是记录本层元素是否重复使用,详细的看卡哥视频吧

本题代码:

 1 class Solution {
2 private:
3 vector<vector<int>> result;
4 vector<int> path;
5 void backtracking(vector<int>& nums, int startIndex) {
6 if (path.size() > 1) {
7 result.push_back(path);
8 }
9 int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]
10 for (int i = startIndex; i < nums.size(); i++) {
11 if ((!path.empty() && nums[i] < path.back())
12 || used[nums[i] + 100] == 1) {
13 continue;
14 }
15 used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了
16 path.push_back(nums[i]);
17 backtracking(nums, i + 1);
18 path.pop_back();
19 }
20 }
21 public:
22 vector<vector<int>> findSubsequences(vector<int>& nums) {
23 result.clear();
24 path.clear();
25 backtracking(nums, 0);
26 return result;
27 }
28 };

46.全排列

卡哥建议:本题重点感受一下,排列问题 与 组合问题,组合总和,子集问题的区别。 为什么排列问题不用 startIndex

 https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html

视频讲解:https://www.bilibili.com/video/BV19v4y1S79W

做题思路:

以[1,2,3]为例,抽象成树形结构如下:

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

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

但排列问题需要一个used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次。如图橘黄色部分所示。

可以看出叶子节点,就是收割结果的地方。那么什么时候,算是到达叶子节点呢?

当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点。

本题代码:

 1 class Solution {
2 public:
3 vector<vector<int>> result;
4 vector<int> path;
5 void backtracking (vector<int>& nums, vector<bool>& used) {
6 // 此时说明找到了一组
7 if (path.size() == nums.size()) {
8 result.push_back(path);
9 return;
10 }
11 for (int i = 0; i < nums.size(); i++) {
12 if (used[i] == true) continue; // path里已经收录的元素,直接跳过
13 used[i] = true;
14 path.push_back(nums[i]);
15 backtracking(nums, used);
16 path.pop_back();
17 used[i] = false;
18 }
19 }
20 vector<vector<int>> permute(vector<int>& nums) {
21 result.clear();
22 path.clear();
23 vector<bool> used(nums.size(), false);
24 backtracking(nums, used);
25 return result;
26 }
27 };

47.全排列 II

卡哥建议:本题 就是我们讲过的 40.组合总和II 去重逻辑 和 46.全排列 的结合,可以先自己做一下,然后重点看一下 文章中 我讲的拓展内容。 used[i - 1] == true 也行,used[i - 1] == false 也行

 https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html

视频讲解:https://www.bilibili.com/video/BV1R84y1i7Tm

做题思路:

这道题目和46.全排列的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。

这里又涉及到去重了。

    还要强调的是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。

以示例中的 [1,1,2]为例 (为了方便举例,已经排序)抽象为一棵树,去重过程如图:

图中我们对同一树层,前一位(也就是nums[i-1])如果使用过,那么就进行去重。

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

    对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!看卡哥文章解释。

本题代码:

 1 class Solution {
2 private:
3 vector<vector<int>> result;
4 vector<int> path;
5 void backtracking (vector<int>& nums, vector<bool>& used) {
6 // 此时说明找到了一组
7 if (path.size() == nums.size()) {
8 result.push_back(path);
9 return;
10 }
11 for (int i = 0; i < nums.size(); i++) {
12 // used[i - 1] == true,说明同一树枝nums[i - 1]使用过
13 // used[i - 1] == false,说明同一树层nums[i - 1]使用过
14 // 如果同一树层nums[i - 1]使用过则直接跳过
15 if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
16 continue;
17 }
18 if (used[i] == false) {
19 used[i] = true;
20 path.push_back(nums[i]);
21 backtracking(nums, used);
22 path.pop_back();
23 used[i] = false;
24 }
25 }
26 }
27 public:
28 vector<vector<int>> permuteUnique(vector<int>& nums) {
29 result.clear();
30 path.clear();
31 sort(nums.begin(), nums.end()); // 排序
32 vector<bool> used(nums.size(), false);
33 backtracking(nums, used);
34 return result;
35 }
36 };

代码随想录算法训练营第二十九天| 491.递增子序列 46.全排列 47.全排列 II的更多相关文章

  1. 代码随想录算法训练营第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

    977.有序数组的平方 :https://leetcode.cn/problems/squares-of-a-sorted-array/ 心得:周末再写... public class Solutio ...

  2. LeetCode算法训练-回溯 491.递增子序列 46.全排列 47.全排列 II

    欢迎关注个人公众号:爱喝可可牛奶 LeetCode算法训练-回溯 491.递增子序列 46.全排列 47.全排列 II LeetCode 491. 递增子序列 分析 找出并返回所有数组中不同的递增子序 ...

  3. 代码随想录算法训练营day01 | leetcode 704/27

    前言   考研结束半个月了,自己也简单休整了一波,估了一下分,应该能进复试,但还是感觉不够托底.不管怎样,要把代码能力和八股捡起来了,正好看到卡哥有这个算法训练营,遂果断参加,为机试和日后求职打下一个 ...

  4. 代码随想录算法训练营day02 | leetcode 977/209/59

    leetcode 977   分析1.0:   要求对平方后的int排序,而给定数组中元素可正可负,一开始有思维误区,觉得最小值一定在0左右徘徊,但数据可能并不包含0:遂继续思考,发现元素分布有三种情 ...

  5. 代码随想录算法训练营day06 | leetcode 242、349 、202、1

    基础知识 哈希 常见的结构(不要忘记数组) 数组 set (集合) map(映射) 注意 哈希冲突 哈希函数 LeetCode 242 分析1.0 HashMap<Character, Inte ...

  6. 代码随想录算法训练营day03 | LeetCode 203/707/206

    基础知识 数据结构初始化 // 链表节点定义 public class ListNode { // 结点的值 int val; // 下一个结点 ListNode next; // 节点的构造函数(无 ...

  7. 代码随想录算法训练营day22 | leetcode 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点

    LeetCode 235. 二叉搜索树的最近公共祖先 分析1.0  二叉搜索树根节点元素值大小介于子树之间,所以只要找到第一个介于他俩之间的节点就行 class Solution { public T ...

  8. 代码随想录算法训练营day18 | leetcode 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树

    LeetCode 513.找树左下角的值 分析1.0 二叉树的 最底层 最左边 节点的值,层序遍历获取最后一层首个节点值,记录每一层的首个节点,当没有下一层时,返回这个节点 class Solutio ...

  9. 代码随想录算法训练营day17 | leetcode ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

    LeetCode 110.平衡二叉树 分析1.0 求左子树高度和右子树高度,若高度差>1,则返回false,所以我递归了两遍 class Solution { public boolean is ...

  10. 代码随想录算法训练营day16 | leetcode ● 104.二叉树的最大深度 559.n叉树的最大深度 ● 111.二叉树的最小深度 ● 222.完全二叉树的节点个数

    基础知识 二叉树的多种遍历方式,每种遍历方式各有其特点 LeetCode 104.二叉树的最大深度 分析1.0 往下遍历深度++,往上回溯深度-- class Solution { int deep ...

随机推荐

  1. 记一次,使用python实现一键在爱发电发布带图片的动态

    1.背景 本人喜欢转载一些youtube上的视频到b站上面,然后就会有些观众想要视频的封面,那我总不可能一个一个发吧,太麻烦了.故打算将资源发布到爱发电上面.但是爱发电却没有公开对应的api,只能自己 ...

  2. ET介绍——组件式设计(优化版的ECS)

    组件式设计 在代码复用和组织数据方面,面向对象可能是大家第一反应.面向对象三大特性继承,封装,多态,在一定程度上能解决不少代码复用,数据复用的问题.不过面向对象不是万能的,它也有极大的缺陷: 1. 数 ...

  3. Blazor实战——Known框架增删改查导

    本章介绍学习增.删.改.查.导功能如何实现,下面以商品资料作为示例,该业务栏位如下: 类型.编码.名称.规格.单位.库存下限.库存上限.备注 1. 前后端共用 1.1. 创建实体类 在KIMS项目En ...

  4. 2021-i春秋秋季赛部分misc+re wp

    两道re比较简单,没有什么好说的,misc的符号执行那题还有点意思,总的来说题目不难 CTFMakesMeAngr 给出了符号执行的中间语言,对着文档把汇编逻辑逆一下,发现是个凯撒加密,位移为14,注 ...

  5. 有JSDoc还需要TypeScript吗

    这听起来是不是很耳熟:你想写一个小型脚本,不管是为页面.命令行工具,还是其他什么类型.你从JavaScript开始,直到你想起写代码时没有类型是多么痛苦.所以你把文件从.js重命名为.ts.然后意识到 ...

  6. ASP.NET Core 6框架揭秘实例演示[36]:HTTPS重定向

    HTTPS是确保传输安全最主要的手段,并且已经成为了互联网默认的传输协议.不知道读者朋友们是否注意到当我们利用浏览器(比如Chrome)浏览某个公共站点的时候,如果我们输入的是一个HTTP地址,在大部 ...

  7. Java中的三元运算,以后用得到!

    前言 Java 中的三元运算,平时也叫做三目运算,大家了解吗?下面就详细介绍一下,以后在项目编程中用得到. 一.Java运算符 在最底层,Java 中的数据是通过使用运算符来操作的.运算符是一种特殊的 ...

  8. 分布式数据库 Join 查询设计与实现浅析

    相对于单例数据库的查询操作,分布式数据查询会有很多技术难题. 本文记录 Mysql 分库分表 和 Elasticsearch Join 查询的实现思路,了解分布式场景数据处理的设计方案. 文章从常用的 ...

  9. TIM-BLDC六步换相-串口中断模拟检测霍尔信号换相-软件COM事件解析

    TIM-BLDC六步换相-串口中断模拟检测霍尔信号换相-软件COM事件解析 一.COM事件解析 COM事件简介:COM事件即换相事件只用于高级定时器当中,其主要目的是用在BLDC方波的控制中,用于同时 ...

  10. 前端Vue自定义简单实用轮播图封装组件 快速实现轮播图

    前端Vue自定义简单实用轮播图封装组件 快速实现轮播图, 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13153 效果图如下: ...