491.递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
- 输入: [4, 6, 7, 7]
- 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
说明:
- 给定数组的长度不会超过15。
- 数组中的整数范围是 [-100,100]。
- 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
思路
该题目与之前求子集的题目略有不同,主要在于不能再使用之前排序然后比较相邻元素的方式来去重。应该采用set之类的容器来辅助去重。
代码-java(set去重)
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracting(nums,0);
return res;
}
private void backtracting(int[] nums,int startIdx){
// 终止条件:path中的元素个数大于等于2
if(path.size()>1){
res.add(new ArrayList(path));
}
Set<Integer> set = new HashSet<>();
for(int i=startIdx;i<nums.length;i++){
// 判断是否递增,如果不是则跳过本层
if(!path.isEmpty()&&nums[i]<path.getLast()){
continue;
}
// 去重
if(!set.isEmpty()&&set.contains(nums[i])){
continue;
}
set.add(nums[i]);
path.add(nums[i]);
backtracting(nums,i+1);
path.removeLast();
}
}
}
46.全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
- 输入: [1,2,3]
- 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
思路
与之前的组合或者子集不同,排列问题的树型图如下。可以看到相同的元素组合,但是元素顺序不同也是算不同的排列。所以下次迭代的开始位置是0。
总结:
- 每层都是从0开始搜索而不是startIndex
- 需要used数组记录path里都放了哪些元素了
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
if(nums.length==0){
return res;
}
used = new boolean[nums.length];
backtracking(nums);
return res;
}
private void backtracking(int[] nums){
// 终止条件:当path中的元素数量等于数组长度
if(path.size()==nums.length){
res.add(new ArrayList(path));
return;
}
// 排列问题元素可重复使用,所以每次i都从0开始
for(int i=0;i<nums.length;i++){
// 判断当前元素是否已经使用过了、
if(used[i]){
continue;
}
used[i] = true;
path.add(nums[i]);
backtracking(nums);
// 回溯
path.removeLast();
used[i] = false;
}
}
}
47.全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
- 输入:nums = [1,1,2]
- 输出: [[1,1,2], [1,2,1], [2,1,1]]
思路
与上一题相比,本题目的数组元素可能包含相同的元素,所以要考虑去重。沿用组合问题去重的思路:对数组元素排序然后相邻元素去重。
代码
class Solution {
//存放结果
List<List<Integer>> result = new ArrayList<>();
//暂存结果
List<Integer> path = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] used = new boolean[nums.length];
Arrays.fill(used, false);
Arrays.sort(nums);
backTrack(nums, used);
return result;
}
private void backTrack(int[] nums, boolean[] used) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
// used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
// used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
// 如果同⼀树层nums[i - 1]使⽤过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
//如果同⼀树⽀nums[i]没使⽤过开始处理
if (used[i] == false) {
used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树枝重复使用
path.add(nums[i]);
backTrack(nums, used);
path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
used[i] = false;//回溯
}
}
}
}