491.递增子序列
题目链接:力扣
这道题找递增子序列,且存在重复的情况,需要去重。看起来和子集II很像。
但问题是因为是要找递增子序列,因此不能先对这个数组进行排序,然后去重的。1.
这道题的树形结构如下:
在图中可以看出,同一父节点下的同层上使用过的元素就不能再使用了 == 树层去重(在代码中是每一个for循环时存入的元素必须是不同的)
但是因为不能进行排序,那就是不能使用之前的去重方式。 - 在单层递归逻辑中使用hashset进行判断去重
回溯三部曲:
1.输出和输入参数:输出 - void; 输入-【两个全局变量 - List<List<Integer>> result;List<Integer> list】;backtracking(int[] nums, int startIndex);
2.终止条件:if(startIndex == nums.length) return;
3.单层循环逻辑:用hashset来储存使用过的元素,for(int i = startIndex; i<nums.length; i++) 如果list的size小于1,则先放入nums[i],并将其储存入hashset中; 若list.size大于1,且hashset中不存在nums[i]则将list.get(list.size()-1)与nums[i]比较,如果nums[i]大于等于list.get(list.size()-1)则将其放入list,并list让入result;backtracking(nums, startIndex+1); 回溯 - list.remove(list.size()-1)
具体代码逻辑:
class Solution {
public List<List<Integer>> findSubsequences(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
backtraking(nums, 0);
return result;
}
List<List<Integer>> result;
List<Integer> list;
public void backtraking(int[] nums, int startIndex){
if(startIndex == nums.length){
return;
}
HashSet<Integer> used = new HashSet<>();
for(int i=startIndex; i<nums.length;i++){
//将不符合条件的排除 - 不是单调递增的/去重 - 注意list是否为空
if(!list.isEmpty() && list.get(list.size()-1) > nums[i] || used.contains(nums[i])){
continue;
}
list.add(nums[i]);
used.add(nums[i]);
//如果list的size为2以上,则放入结果集
if(list.size()>1){
result.add(new ArrayList<>(list));
}
backtraking(nums, i+1);
//回溯
list.remove(list.size()-1);
}
}
}
注意题目中说了,数值范围[-100,100],所以完全可以用数组。
用数组来做哈希,效率就高了很多。 ---- 数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如果数值范围小的话能用数组尽量用数组。
具体优化之后的代码:
class Solution {
public List<List<Integer>> findSubsequences(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
backtraking(nums, 0);
return result;
}
List<List<Integer>> result;
List<Integer> list;
public void backtraking(int[] nums, int startIndex){
if(startIndex == nums.length){
return;
}
//采用数组进行hash去重
int[] used = new int[201];
for(int i=startIndex; i<nums.length;i++){
//排除不符合的情况
if(!list.isEmpty() && list.get(list.size()-1) > nums[i] || used[nums[i]+100] == 1){
continue;
}
list.add(nums[i]);
used[nums[i]+100] = 1;
//放入结果集
if(list.size()>1){
result.add(new ArrayList<>(list));
}
//递归
backtraking(nums, i+1);
//回溯
list.remove(list.size()-1);
}
}
}
参考资料:代码随想录
46.全排列
题目链接:力扣
排列问题和组合、分割、子集不同;排列中[1,2]和[2,1]是不同的结果。
以[1,2,3]为例,抽象成树形结构如下:
回溯三部曲:
1.输出以及输入参数:输出 - void;输入 - 【两个全局变量 - List<List<Integer>> result,List<Integer> list】,backtracking(int[] nums, int[] used - 记录当前被使用的元素,防止从头遍历的时候重复获取)
2.终止条件:如果list.size()==nums.length,将list放入result。
3.单层循环逻辑: for(int i=0; i<nums.length; i++)【对于排列来说,不存在index,因此每次for循环都需要从0开始遍历】 如果used[i] == 0, 将nums[i]放入list,将used[i]==1;backtracking(nums, used); 回溯 - used[i] == 0, 将num[i]从list中移除。
具体代码实现:
class Solution {
public List<List<Integer>> permute(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
//使用used记录放入list中的元素
int[] used = new int[nums.length];
backtraking(nums, used);
return result;
}
List<List<Integer>> result;
List<Integer> list;
public void backtraking(int[] nums, int[] used){
//终止条件
if(list.size() == nums.length){
result.add(new ArrayList<>(list));
return;
}
//每层for循环都是从index为0的地方开始遍历
for(int i=0; i<nums.length; i++){
if(used[i] == 1){
continue;
}
list.add(nums[i]);
used[i] = 1;
//递归
backtraking(nums, used);
//回溯
list.remove(list.size()-1);
used[i] = 0;
}
}
}
参考资料:代码随想录
47.全排列 II
题目链接:力扣
这道题唯一的区别就是存在重复的元素。
因为所有的顺序都会考虑进去,因此和组合一样,在对数组进行排序之后,并不会影响最后的结果集。
去重的考虑也是一样的,对树层进行去重 - 在同一for循环,如果是有选取过相同的元素则不需要重复选取。
具体的树形结构图如下:
回溯三部曲:
1.输出以及输入参数:输出 - void;输入 - 【两个全局变量 - List<List<Integer>> result,List<Integer> list】,backtracking(int[] nums, int[] used - 记录当前被使用的元素,防止从头遍历的时候重复获取 - 【也可以直接当做全局变量】)
2.终止条件:如果list.size()==nums.length,将list放入result。
3.单层循环逻辑: for(int i=0; i<nums.length; i++)【对于排列来说,不存在index,因此每次for循环都需要从0开始遍历】 如果(i>0 && nums[i] == nums[i-1] && used[i-1]==1) 跳过;如果used[i] == 0, 将nums[i]放入list,将used[i]==1;backtracking(nums, used); 回溯 - used[i] == 0, 将num[i]从list中移除。
具体代码如下:
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
result = new ArrayList<>();
list = new ArrayList<>();
used = new int[nums.length];
//排序
Arrays.sort(nums);
backtraking(nums);
return result;
}
List<List<Integer>> result;
List<Integer> list;
int[] used;//可以将used作为全局变量
public void backtraking(int[] nums){
if(list.size() == nums.length){
result.add(new ArrayList<>(list));
return;
}
//0开始遍历
for(int i=0; i<nums.length; i++){
//去重
if(i>0 && nums[i] == nums[i-1] && used[i-1] == 1){
continue;
}
if(used[i] == 1){
continue;
}
list.add(nums[i]);
used[i] = 1;
//递归
backtraking(nums);
//回溯
list.remove(list.size() -1);
used[i] = 0;
}
}
}
参考资料:代码随想录