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]。
给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
难点:
在90.子集II中我们是通过排序,再加一个标记数组来达到去重的目的。
而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。
所以不能使用之前的去重逻辑!
思路:
每层标记元素的使用情况,被使用的元素不能再次使用
在path大小大于1的时候收集
这里注意,-100 <= nums[i] <= 100,所以在构造标记数组的时候要小心(注意负数,同时防止越界访问)
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
if (nums == null || nums.length == 0) return result;
backtracking(nums, 0);
return result;
}
public void backtracking(int[] nums, int startId) {
if (path.size() > 1) {
result.add(new ArrayList<>(path));
}
//注意-100 <= nums[i] <= 100
boolean[] used = new boolean[201];
for (int i = startId; i < nums.length; i++) {
if (!path.isEmpty() && nums[i] < path.get(path.size()-1) || used[nums[i]+100]) continue;
used[nums[i]+100] = true;
path.add(nums[i]);
backtracking(nums, i+1);
path.remove(path.size()-1);
}
}
}
时长:
1h
收获:
对于养成思维定式或者套模板套嗨了的同学,这道题起到了很好的警醒作用。更重要的是拓展了大家的思路!
46. 全排列
题目链接
题目描述:
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
- 输入: [1,2,3]
- 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
难点:
排列问题
排列问题需要一个used数组,标记已经选择的元素
思路:
因为元素的使用不能重复,
在当前层遍历的时候,如果当前元素已经收入path,则跳过这个元素
收集叶子节点的结果
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
boolean[] used = new boolean[nums.length];
backtracking(nums, used);
return result;
}
public void backtracking(int[] nums, boolean[] used) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
}
for (int i = 0; i < nums.length; i++) {
if (used[i] == true) continue; //递归地分支中,不允许被使用过的元素重复使用,否则叶子上的结果就是错的
used[i] = true;
path.add(nums[i]);
backtracking(nums, used);
path.remove(path.size()-1);
used[i] = false;
}
}
}
时长:
20min
收获:
排列问题的基本思路
47. 全排列 II
题目链接
题目描述:
给定一个可包含重复数字的序列 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
难点:
强调!是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。
思路:
排序排序排序!
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.fill(used, false);
Arrays.sort(nums);
backtracking(nums, used);
return result;
}
public void backtracking(int[] nums, boolean[] used) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
}
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i-1] == nums[i] && !used[i-1]) continue; //去重,防止结果集中有相同结果
if (used[i] == true) continue; //防止一个叶子结果中重复使用某个元素
used[i] = true;
path.add(nums[i]);
backtracking(nums, used);
path.remove(path.size()-1);
used[i] = false;
}
}
}
时长:
20min
收获:
在排列问题中,如何对结果集中的结果去重