491.递增子序列
本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。
没法排序去重的问题,就需要一个set来记录同一父节点下的同层上使用过的元素(使用过的就不能在使用了)
因为没有排序,所以不能根据位置来去重(nums[i]!=nums[i-1])所以要用一个单独的set来记录
子集/组合问题(没有顺序的)都要考虑同层的重复性
优化:由于本题nums[i]范围是-100-100,其实可以用数组来代替hashset,效率更高
class Solution {
private List<Integer> path = new ArrayList<>();
private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums,0);
return res;
}
private void backtracking (int[] nums, int start) {
if (path.size() > 1) {
res.add(new ArrayList<>(path));
}
int[] used = new int[201];
for (int i = start; i < nums.length; i++) {
if (!path.isEmpty() && nums[i] < path.get(path.size() - 1) ||
(used[nums[i] + 100] == 1)) continue;
used[nums[i] + 100] = 1;
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
46.全排列
达到叶子节点==>path.size()==nums.length
由于是顺序所以不用startIndex,而是每次从0开始搜索,然后用一个专门的used/visited数组来记录那些遍历过,达到去重
class Solution {
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0){
return result;
}
used = new boolean[nums.length];
permuteHelper(nums);
return result;
}
private void permuteHelper(int[] nums){
if (path.size() == nums.length){
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++){
if (used[i]){
continue;
}
used[i] = true;
path.add(nums[i]);
permuteHelper(nums);
path.removeLast();
used[i] = false;
}
}
}
47.全排列 II
本题涉及到排列的去重,去重的题,如果可以的话,先对题目进行排序,方便我们去重
一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。
图源:代码随想录
如果要对树层中前一位去重,就用used[i - 1] == false,如果要对树枝前一位去重用used[i - 1] == true
也可以理解为,有重复多个数字,只让这些数字直接有一种顺序比如有三个1:1a,1b,1c
通过nums[i]==nums[i-1]&&used[i-1]=true时才能继续for循环,否则continue,这样就确定三个1的顺序一定是1a,1b,1c 在这三个1内没有排序的可能,否则可能会有六种排序方式