前言:
如果你一点也不了解什么叫做回溯算法,那么推荐你看看这一篇回溯入门,让你快速了解回溯算法的基本原理及框架
递增子序列
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
来源:力扣(LeetCode)递增子序列
由题意可得下面两点要求:
- 递增的子序列,且元素数量大于2
- 子序列与子序列不能相同
- 可使用重复出现的数字
像这种需要依次取元素,然后将元素存储起来汇成总集合,期间可能还需要回退取不一样集合的题目,我们第一个想到的可以使用回溯法,那么该如何回溯呢?且看下图分析:我们使用[ 4,7,6,7 ]举例
- 回溯收集子集条件
根据题意可以得知,我们只要子序列的数量大于等于2就可以 - 回溯终止条件
终止条件就是达到nums.size() - 单层搜索逻辑
我们由图可以得知,虽然序列里面可能有重复的数字,但是单层我们不能取相同的数字,如果我们取了相同的数字,那么必定会存在相同的子集,所以我们单层需要去重
单层去重我们这里使用一个标记的容器 unordered_set< int > use存放已经出现过的数字
代码如下:
class Solution {
public:
vector<vector<int>> arr;
vector<int> _arr;
void BackTracking(vector<int>& nums,int begin)
{
if(_arr.size()>=2)//只要数据>=2就存储,我们这里不需要return
{
arr.push_back(_arr);
}
unordered_set<int> use;//标记容器
for(int i=begin;i<nums.size();i++)
{
//如果是空直接存放,然后判断别的关系
if((!_arr.empty() && _arr.back()>nums[i]) || use.find(nums[i])!=use.end())
{
continue;
}
_arr.push_back(nums[i]);
use.insert(nums[i]);
BackTracking(nums,i+1);//不能重复使用单个数据,所以我们需要i+1
_arr.pop_back();
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
BackTracking(nums,0);
return arr;
}
};
全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
来源:力扣(LeetCode)全排列 II
本题是全排列的进阶版,之前是没有重复元素,现在有重复元素,我们该如何解决呢?
这个题与上一题递增子序列相差不多,也是需要单层去重,且看下图:
相较于之前的收集元素,排列我们需要将每个元素都使用到,所以我们每次循环开始条件都为0,但是为了不使用一个使用过的元素,我们需要设置一个标记的数组,使用过一个标记一个,单层去重,是因为同一层使用相同的元素没有意义,使用相同元素,相当于递归两遍相同数据,导致出现相同子集
- 先排序所有元素
- 标记数组
- 单层去重
代码如下:
class Solution {
public:
vector<vector<int>> arr;
vector<int> _arr;
void BackTracking(vector<int>& nums,vector<bool>& use)
{
if(_arr.size()==nums.size())
{
arr.push_back(_arr);
return;
}
for(int i=0;i<nums.size();i++)
{
//单层去重,及判断元素是否使用过
if(i>0 && nums[i]==nums[i-1] && use[i-1]==false)
{
continue;
}
if(use[i]==false)
{
use[i]=true;
_arr.push_back(nums[i]);
BackTracking(nums,use);
_arr.pop_back();
use[i]=false;
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(),nums.end());//需要排序,为去重做准备
vector<bool> use(nums.size(),false);
BackTracking(nums,use);
return arr;
}
};