递增子序列
这题也要求子集, 要求去重, 所以和子集II是很相似的, 但是这里不能用sort方法, 否则就改变元素的顺序了
归根结底, 还是对于树层上的去重(但是这里不能sort, 所以不能用之前的boolean used数组)
- 同样这里结果也是在节点上, 只不过要大于等于二
- 单层递归要砍掉的: 如果下一个数小于path最右的数
我没有搞懂这个+100是什么意思, 周末必须要吃透
class Solution {
List<Integer> path = new ArrayList<>();
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() >= 2){
res.add(new ArrayList<>(path));
//注意这里不需要return, 因为要取所有的节点, 和之前那道题同理
}
int uset[] = new int[201];
for(int i = start; i < nums.length; i++){
//判断从大到小
if(!path.isEmpty() && nums[i] < path.get(path.size() - 1) || (uset[nums[i] + 100] == 1)){
continue;
}
uset[nums[i] + 100] = 1;
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
全排列
排列是有序的, 而且处理排列问题就不用使用startIndex了
- 终止条件: 就是当path大小和nums大小一样时
- 这里需要一个boolean used数组, 记录此时path里有哪些元素使用过了, 一个排列里只能用一次
- 就是注意不用startIndex,每次回溯要把used数组改为false
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
//注意一开始定义boolean数组
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
//这里可以进行一个剪枝
if(nums.length == 0){
return res;
}
//定义一个新的used数组
used = new boolean[nums.length];
backtracking(nums);
return res;
}
//注意这里就完全不用start了
private void backtracking(int[] nums){
//先写终止条件, 就是长度一样
if(path.size() == nums.length){
res.add(new ArrayList<>(path));
return;
}
//开始回溯,注意不用start了
for(int i = 0; i < nums.length; i++){
//要用used数组判断一下
if(used[i]){
continue;
}
//这里别写错了。 是要先改为true
used[i] = true;
path.add(nums[i]);
backtracking(nums);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
全排列II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列(说明多了个去重而已)
强调的是去重一定要对元素进行排序
组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果
如果要对树层中前一位去重,就用used[i - 1] == false
,如果要对树枝前一位去重用used[i - 1] == true
(所以建议在初级阶段,直接无脑用false就行了)
对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!
(这里判定used为true or false都可以, 但是数层效率更高, 所以用false)
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
//别忘记要把used数组全部变成false
Arrays.fill(used, false);
//也别忘了排序
Arrays.sort(nums);
backtracking(nums, used);
return res;
}
//这里不一定需要used数组作为参数
public void backtracking(int[] nums, boolean[] used){
if(path.size() == nums.length){
res.add(new ArrayList<>(path));
return;
}
for(int i = 0; i < nums.length; i++){
//直接按照used数组判断, 使用过就跳过
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
//如果同一树枝没使用过就开始处理(没被标记过)
if(used[i] == false){
used[i] = true;
path.add(nums[i]);
backtracking(nums, used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
}