题目链接: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中可以利用排序来去重,但是本题要求自增子序列,所以是不能排序的。
首先本体要求子序列,所以元素是不能重复的,需要用传递参数startIndex来调整下一层递归的起始位置。
其次每层递归当中也有可能出现相同的元素,所以我们要对每层递归的元素进行去重,我们用HashSet。
代码如下:
class Solution {
List>result = new ArrayList<>();//收集结果(递增子序列)
LinkedListpath = new LinkedList<>();//遍历所有递增子序列
int len;//数组长度
public void backTravel(int[] nums, int index) {
if(path.size() >= 2) result.add(new LinkedList<>(path));//收集递增子序列
HashSet set = new HashSet();//对本层递归的元素进行去重
for(int i = index; i < len; i++) {
if((path.size() != 0 && nums[i] < path.getLast()) || set.contains(nums[i])) continue;
else {
path.add(nums[i]);
set.add(nums[i]);//记录这个元素本层已经用过了,后面不能再用
backTravel(nums, i + 1);//递归
path.removeLast();//回溯
}
}
}
public List> findSubsequences(int[] nums) {
len = nums.length;
backTravel(nums, 0);
return result;
}
}
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1] 输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同算法分析:
这道题的去重操作要在path中进行。
递归传递参数只需要nums数组即可。
结束条件:path长度等于数组长度时,表明有一种排列方法了,将其放入结果集,然后结束递归。
for循环遍历数组,同时判断当前元素是否在path中出现过,如果没出现将其放入path中,同时标记其已存在path,然后递归,后在回溯(path中删除该元素,同时消除标记)。
代码如下:
class Solution {
List>result = new ArrayList<>();//收集全排列的结果
LinkedListpath = new LinkedList<>();//遍历每一种排列
int len;//数组长度
boolean[] used = new boolean[21];//记录path中出现的元素,对path进行去重
public void backTravel(int[] nums) {
if(path.size() == len) {//如果path的长度等于数组的长度,说明有一种排列组合了,将其放入结果集
result.add(new LinkedList<>(path));
return;
}
for(int i = 0; i < len; i++) {//从下标0开始横向遍历数组,判断每个元素是否在path中出现过
if(used[i] == true) continue;
//如果该元素还没在path中出现,将其放入path同时对其进行标记,表明已在path中
path.add(nums[i]);
used[i] = true;
backTravel(nums);//递归
//回溯
path.removeLast();//path中删除最后一个元素
used[i] = false;//同时消除其标记,表明已从path中去除
}
}
public List> permute(int[] nums) {
len = nums.length;
backTravel(nums);
return result;
}
}
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
算法分析:
这道题因数组中包含重复元素,所以要进行两重去重操作。
第一重去重:对path上的元素进行去重(注意是对同一个元素去重,其他值相等的元素不算同一个元素)。
第二重去重:每一层递归当中,对与使用过的元素相等元素进行去重(可以是值相等的不同元素)。
代码如下:
class Solution {
List>result = new ArrayList<>();//收集结果集
LinkedListpath = new LinkedList<>();//遍历每一种排序
int len;//数组长度
boolean[] used = new boolean[21];//记录每个元素在path上是否出现过,一遍对path去重
public void backTravel(int[] nums) {
if(path.size() == len) {//如果path长度等于数组长度,说明找到一种排列,将其放入结果集,结束递归
result.add(new LinkedList<>(path));
return;
}
HashSetset = new HashSet();//同层递归对使用过的相同元素进行去重(注意这儿的去重是对于值相同的元素,而used是对同一个元素去重)
for(int i = 0; i < len; i++) {
//如果当前元素在path存在或者在这一层递归中使用过值相等的元素,直接进行下一层for循环
if(used[i] == true || set.contains(nums[i])) continue;
path.add(nums[i]);//将该元素插入path
used[i] = true;//该元素标记在path中使用过
set.add(nums[i]);//标记在这一层递归中使用过
backTravel(nums);//进行下一层递归
//回溯,
path.removeLast();//从path中拿出该元素
used[i] = false;//标记path中没有该元素
}
}
public List> permuteUnique(int[] nums) {
len = nums.length;
backTravel(nums);
return result;
}
}
要注意区分不同情况下对元素的不同去重操作。