491.递增子序列
该问题最初乍一看不就是 90.子集II 更改一下剪枝条件
实际上有坑,题目要求自增的子序列,所以不能移动给定数组位置(就是不能进行排序剪枝)
所以需要考虑用其他方法来记录重复元素的遍历 -> 利用哈希集合set
决策树如下:
此时可知:同一父节点下的同层上使用过的元素就不能够再次使用了
所以剪枝条件如下:
// 剪枝条件
if(!track.isEmpty() && track.get(track.size() - 1) > nums[i]){
continue;
}
if(used.contains(nums[i])){
continue;
}
完整代码如下:
class Solution {
List<List<Integer>> res = new LinkedList<>();
List<Integer> track = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtrack(nums, 0);
return res;
}
public void backtrack(int[] nums, int start){
// 利用哈希集合防止选择同层相同元素
HashSet<Integer> used = new HashSet<>();
// 由于题目要求递增子序列中至少有两个元素
if(track.size() >= 2){
res.add(new LinkedList<>(track));
// 不 return 是因为要取树上大于2个元素的所有节点,当循环结束时会自动返回
}
for(int i = start; i < nums.length; i++){
// 剪枝条件
if(!track.isEmpty() && track.get(track.size() - 1) > nums[i]){
continue;
}
if(used.contains(nums[i])){
continue;
}
used.add(nums[i]);
track.add(nums[i]);
backtrack(nums, i + 1);
track.remove(track.size() - 1);
}
}
}
46.全排列
由于经历过了前面的组合,子集问题磨炼,一看见该类型题便有了思路并尝试自己画决策树
发现两个不同点:
- 由于全排列有序,每层都是从0开始搜索而不是startIndex
- 需要记录路径里都放了哪些元素了,防止重复选择 -> 利用boolean数组进行记录,同时一起进行回溯
所以直接给出完整代码:
class Solution {
List<List<Integer>> res = new LinkedList<>();
List<Integer> track = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
// 用于记录此时路径中已经被使用的元素,一个排列里一个元素只能使用一次
boolean[] used = new boolean[nums.length];
backtrack(nums, used);
return res;
}
public void backtrack(int[] nums, boolean[] used){
if(track.size() == nums.length){
res.add(new LinkedList<>(track));
return;
}
// 由于排列是有序的,所以需要从数组开始选择
for(int i = 0; i< nums.length; i++){
// 遇见该路径上被使用过的数,直接跳过
if(used[i] == true){
continue;
}
used[i] = true;
track.add(nums[i]);
backtrack(nums, used);
track.remove(track.size() - 1);
used[i] = false;
}
}
}
47.全排列 II
该题便是在全排列问题中如何去重:一个可包含重复数字的序列,要返回所有不重复的全排列。
决策树如下:
所以由此可知需要在同树层剪枝去重:
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
在此当中 used 数组用于判断路径元素是否使用过
注:used[i - 1] == false 用于确保是在同一树层当中去重
完整代码如下:
class Solution {
List<List<Integer>> res = new LinkedList<>();
List<Integer> track = new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] used = new boolean[nums.length];
Arrays.sort(nums);
backtrack(nums, used);
return res;
}
public void backtrack(int[] nums, boolean[] used){
if(track.size() == nums.length){
res.add(new LinkedList<>(track));
return;
}
for(int i = 0; i < nums.length; i++){
if(used[i] == true){
continue;
}
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
used[i] = true;
track.add(nums[i]);
backtrack(nums, used);
track.remove(track.size() - 1);
used[i] = false;
}
}
}