491.递增子序列
讲解链接:代码随想录-491.递增子序列
public List<List<Integer>> findSubsequences(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
backtracking(nums, 0, new ArrayList<>(), results);
return results;
}
void backtracking(int[] nums, Integer startIndex, List<Integer> paths, List<List<Integer>> results) {
if (paths.size() >= 2) {
results.add(new ArrayList<>(paths));
}
// 用来排除这一层中相同的元素
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = startIndex; i < nums.length; i++) {
if (paths.size() > 0 && paths.get(paths.size() - 1) > nums[i]) {
continue;
}
// 如果数字已经在当前层中使用过,就跳过
if (map.getOrDefault(nums[i], 0) >= 1) {
continue;
}
map.put(nums[i], 1);
paths.add(nums[i]);
backtracking(nums, i + 1, paths, results);
paths.remove(paths.size() - 1);
}
}
46.全排列
讲解链接:代码随想录-46.全排列
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
backtracking(nums, new HashMap<>(), new ArrayList<>(), results);
return results;
}
void backtracking(int[] nums, Map<Integer, Boolean> usedMap, List<Integer> paths, List<List<Integer>> results) {
if (paths.size() == nums.length) {
results.add(new ArrayList<>(paths));
return;
}
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (usedMap.getOrDefault(i, false)) continue;
usedMap.put(i, true);
paths.add(num);
backtracking(nums, usedMap, paths, results);
paths.remove(paths.size() - 1);
usedMap.remove(i);
}
}
47.全排列 II
讲解链接:代码随想录-47.全排列 II
树层上去重(used[i - 1] == false),的树形结构如下:
树枝上去重(used[i - 1] == true)的树型结构如下:
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] used = new boolean[nums.length];
Arrays.fill(used, false);
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
backTrack(nums, used, new ArrayList<>(), result);
return result;
}
private void backTrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> result) {
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, result);
path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
used[i] = false;//回溯
}
}
}
总结
一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。