Day25 回溯算法 491.递增子序列 46.全排列 47.全排列Ⅱ

2,162 阅读3分钟

491.递增子序列

题目链接:491.递增子序列

难度指数:😀😐😕

有些题解说这道题是深搜。其实所有的回溯算法,你都可以说它是深搜;所有的深搜,你都可以说它是递归;

本题,我们做一个更细致的分类,认为它还是属于回溯算法。(当然,你说是深搜也没毛病)

和子集问题有点像,但又处处是陷阱

本题和刚做过的 90.子集Ⅱ 非常像,但又很不一样,很容易掉坑里。

😮本题是求自增子序列,❌不可以对原数组进行排序。

⚠️不能使用之前的去重逻辑!!

依旧是每个节点都是我们要收集的结果

树形结构:

25.01.png

代码思路:

去重做的是树层上的去重;

树枝上去取不同的元素,这些元素有可能数值相同。(即树枝上可以取相同数值,没毛病 )

 vector<vector<int>> result;  //存放子集的结果集
 vector<int> path;  //存放子集
 void backtracking(vector<int> nums, int startIndex) {
     //(可以不用终止条件)
     //要求递增子序列的大小至少为2
     if (path.size() > 1) {
         result.push_back(path);
         //注意这里不要return,要取树上的节点
     }
     //单层搜索的逻辑
     unordered_set<int> 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();
     } 
 }

AC代码: (核心代码模式)

 class Solution {
 private:
     vector<vector<int>> result;  //存放子集的结果集
     vector<int> path;  //存放子集
     void backtracking(vector<int>& nums, int startIndex) {
         //(不用写终止条件)
         //要求递增序列的大小至少为2
         if (path.size() > 1) {
             result.push_back(path);
             //注意这里不要return,要取树上的节点
         }
         //单层搜索的逻辑
         unordered_set<int> 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<vector<int>> findSubsequences(vector<int>& nums) {
         result.clear();
         path.clear();
         backtracking(nums, 0);
         return result;
     }
 };

哈希优化:

46.全排列

题目链接:46.全排列

难度指数:😀😐🤨

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

处理排列问题就不用使用 startIndex 了🤗。

25.02.png

代码思路:

因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。

而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次

 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;
         }
         used[i] = true;
         path.push_back(nums[i]);
         backtracking(nums, used);  //回溯
         path.pop_back();
         used[i] = false;
     }
 }

AC代码: (核心代码模式)

 class Solution {
     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) {  //path里已经收录的元素,直接跳过
                 continue;
             }
             used[i] = true;
             path.push_back(nums[i]);
             backtracking(nums, used);  //递归
             path.pop_back();  //回溯
             used[i] = false;
         }
     }
 public:
     vector<vector<int>> permute(vector<int>& nums) {
         result.clear();
         path.clear();
         vector<bool> used(nums.size(), false);
         backtracking(nums, used);
         return result;
     }
 };

47.全排列Ⅱ

题目链接:47.全排列Ⅱ

难度指数:😀😐🤨

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

25.03.png

代码思路:

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

AC代码: (核心代码模式)

 class Solution {
 private:
     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 (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;
             }
         }
     }
 public:
     vector<vector<int>> permuteUnique(vector<int>& nums) {
         result.clear();
         path.clear();
         sort(nums.begin(), nums.end());  //排序
         vector<bool> used(nums.size(), false);
         backtracking(nums, used);
         return result;
     }
 };