491.递增子序列
在子集中我们是通过排序,再加一个标记数组来达到去重的目的。而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用之前的去重逻辑。本题抽象为树结构过程如下:
从上图可以看到,如果在同一个父节点下,同一树层使用过的元素便不再取,如果所取元素小于子序列最后一个元素,也不符合条件。因此,可以设置一个哈希集合,来记录当前所取的值是否被使用过,哈希集合在递归后不用回溯,因为记录的是同一树层的使用情况,新的循环会清空重新记录。
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public void backTracking(int[] nums, int startIdx){
if(path.size()>1){
res.add(new ArrayList<>(path));
}
HashSet record= new HashSet<>();
for(int i=startIdx;i<nums.length;i++){
/*
如果path不为空且当前元素小于path最后一个元素,则不取
如果元素的值使用过,不取
*/
if((!path.isEmpty() && path.get(path.size()-1)>nums[i]) || record.contains(nums[i])){
continue;
}
record.add(nums[i]);
path.add(nums[i]);
backTracking(nums, i+1);
path.removeLast();
}
}
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums, 0);
return res;
}
}
46.全排列
排列问题不需要使用startIdx因为可以存在重复取值的情况,比如第一次1被取过,形成[1,2,3],后面还可以再取组成[2,1,3],但是需要设置一个数组used来记录当前元素在同一path中是否使用过。如果used[i-1]为true,则取下一个元素,因为本题为不重复的元素,所以不用去重。
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
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[i-1]为true,说明同一树枝上使用过值一样的元素
如果used[i-1]为false,说明同一树层上使用过值一样的元素
*/
if(used[i]==true) continue;
used[i]=true;
path.add(nums[i]);
backTracking(nums, used);
path.removeLast();
used[i]=false;
}
}
public List<List<Integer>> permute(int[] nums) {
boolean[] used=new boolean[nums.length];
backTracking(nums, used);
return res;
}
}
📢注意:在调用回溯算法的时候,要记得创建一个used数组。
47.全排列 II
本题和上一题的区别在于有重复的数字,所以需要去重,先对数组进行排序,设置used数组记录是否使用过如果used[i-1]为true,说明同一树枝上使用过值一样的元素;如果used[i-1]为false,说明同一树层上使用过值一样的元素。
import java.util.Arrays;
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] 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[i-1]== used[i]时
used[i-1]为true,说明同一树枝使用过
若为false,说明同一树层使用过
*/
if(used[i]==true || (i>0 && nums[i-1]==nums[i] && used[i-1]==false)){
continue;
}
used[i]=true;
path.add(nums[i]);
backTracking(nums, used);
path.removeLast();
used[i]=false;
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
used=new boolean[nums.length];
backTracking(nums, used);
return res;
}
}