首页 > 编程语言 >代码随想录算法训练营第二十九天| 491.递增子序列 46.全排列 47.全排列 II

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

时间:2023-09-03 21:35:25浏览次数:53  
标签:vector 排列 nums 46 随想录 used result path

 

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 };

 

 

标签:vector,排列,nums,46,随想录,used,result,path
From: https://www.cnblogs.com/romantichuaner/p/17675419.html

相关文章

  • 代码随想录算法训练营第三十天| 51. N皇后 37. 解数独 总结
        卡哥建议:今天这三道题都非常难,那么这么难的题,为啥一天做三道? 因为 一刷 也不求大家能把这么难的问题解决,所以 大家一刷的时候,就了解一下题目的要求,了解一下解题思路,不求能直接写出代码,先大概熟悉一下这些题,二刷的时候,随着对回溯算法的深入理解,再去解决如下三题。 ......
  • 代码随想录算法训练营第二十八天| 93.复原IP地址 78.子集 90.子集II
     93.复原IP地址    卡哥建议:本期本来是很有难度的,不过 大家做完 分割回文串 之后,本题就容易很多了   题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html   视频讲解:https://www.bilibili.com/video/BV1XP4......
  • 代码随想录算法训练营第二十七天| 39. 组合总和 40.组合总和II 131.分割回文串
      39. 组合总和    卡哥建议:本题是 集合里元素可以用无数次,那么和组合问题的差别 其实仅在于 startIndex上的控制   题目链接/文章讲解:https://programmercarl.com/0039.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C.html   视频讲解:https://www.bilibili.com......
  • 代码随想录算法训练营第二十五天| 216.组合总和III 17.电话号码的字母组合
     216.组合总和III    卡哥建议:如果把 组合问题理解了,本题就容易一些了。    题目链接/文章讲解:https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html   视频讲解:https://www.bilibili.com/video/BV1wg411873x  做题思路:......
  • 【9月摸鱼计划】x2240处理器与x4605e处理器比较
    这两款处理器适合不同的场景和需求。如果需要高性能的服务器处理器来运行复杂的数据任务,则X4605E处理器是更好的选择。如果需要一款低功耗的家庭或办公用途台式机处理器,则X2240更适合。X4605E处理器和X2240处理器是来自英特尔(Intel)和AMD公司的两款不同类型的处理器,它们都具有不同......
  • [代码随想录]Day34-动态规划part02
    题目:62.不同路径思路:首先想到的是数论方法组合数其实就是向右和向下的步数是固定的,就找一个组合的个数就可以了。状态转移方程:一个位置的路径数就是,上面位置和左面位置路径数的和按照动规五部曲来分析:确定dp数组(dptable)以及下标的含义:dp[i][j]:表示从(0,0)出发,到(i,j)有d......
  • P1463 [POI2001] [HAOI2007] 反素数 题解
    P1463[POI2001][HAOI2007]反素数题解可以发现,最大的不超过\(n\)的反素数就是\(1\simn\)中因数最多的数字。证明:设\(x,x\in[1,n]\)为\(1\simn\)中因数最多的数字,则\(x<y\len\)都不会成为答案,因为存在\(x<y\)因数比\(y\)多,同时也不会存在\(y'<x\)......
  • 代码随想录——数组篇
    二分查找题目链接注意:求均值防溢出,left+(right-left)/2等价于(left+right)/2。原地移除元素题目链接给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入......
  • COMP4620/8620 AI算法分析
    COMP4620/8620–AdvancedTopicsinAIDecision-makingunderUncertaintyinRoboticsSemester-22023–Assignment1Duedate:Monday,4September202323:59CanberratimeConvenor&Lecturer:HannaKurniawatiUpdate29/08/23:•Nochangesinthetext,......
  • [代码随想录]Day33-动态规划part01
    题目:509.斐波那契数思路:动规五部曲:这里我们要用一个一维dp数组来保存递归的结果确定dp数组以及下标的含义dp[i]的定义为:第i个数的斐波那契数值是dp[i]确定递推公式为什么这是一道非常简单的入门题目呢?因为题目已经把递推公式直接给我们了:状态转移方程dp[i]=dp[i-......