LeetCode491递增子序列
去重+排序
思路:对于终止条件:可以使用temp.size()>=2来判断。但是这里只需要ans.add 即可,不可以return。return会吧大于2的都丢弃。
在for里面的横向遍历的时候,还要判断去重和递增。
至于去重 的时候使用了辅助的map或者数组来做。是因为这里的nums不是递增的,也不可以变成递增的。只能用map来记录是否使用过。
所以使用map是标准的用法。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return ans;
}
public void backtracking(int[] nums, int index){
if(temp.size() >1){
ans.add(new ArrayList(temp));
//return; 这里不能加return 因为这里return的话只会把=2的结果返回
}
// 区别在于这里需要一个变量来当前元素是否被使用过
// 使用过就不会放在第一个位置了
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=index;i<nums.length;i++){
if(!temp.isEmpty() && nums[i] < temp.getLast()){
continue;
}
if(map.getOrDefault(nums[i], 0) == 1){ // 如果当前元素用过了 后面就不会再用
// 之所以使用map是因为这里不是递增的数组,没法用nums[i] == nums[i-1]来判断了
// getOrDefault:如果有着key就返回对应的值,没有就返回0
// getOrDefault是为了保证不出错
continue;
}
// map.put(nums[i],map.getOrDefault( nums[i],0 )+1);// getOrDefault是为了保证不出错
map.put(nums[i], 1); //
temp.add(nums[i]);
backtracking(nums, i+1);
temp.removeLast();
}
}
}
LeetCode46全排列
全排列的几个关键点:
- 用一个used数组来标记那些数组已经使用过
- 不在用index来作为for的开始,丢弃一些元素了,而是每次从0开始。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
boolean[] used; // 注意不要写成BooLean
public List<List<Integer>> permute(int[] nums) {
// 排列问题
used = new boolean[nums.length];
backtracking(nums);
return ans;
}
public void backtracking(int[] nums){
// 终止条件
// 因为只返回叶子节点所以用长度来做一个判断
// 同时要加上return
if(nums.length == temp.size()){
ans.add(new ArrayList(temp));
return;
}
for(int i=0;i<nums.length;i++){ // 这里每次各分支都要从头开始的
// 用used表示那些元素使用过了。
// 这里找的是排列且从头开始 一定会遇到重复的情况。首先1,选过了再递归里面还是从1开始就跳过1,去2,3里面找,找的时候也是会从1开始,遇到用过的就跳过。
// 例如3,for i到了3 此时结果只有[3, , ]。进入递归,还是从头开始找,这时1没有用就会被加到里面变成[3, 1, ] 接着加2 ,在回溯找到[3, 2, 1]
if(used[i]){
continue;
}
used[i] = true;
temp.add(nums[i]);
backtracking(nums);
used[i] = false;
temp.removeLast();
}
}
}
LeetCode47全排列2
used[i-1]很关键。
对于排列问题的剪枝去重,去除重复的排列。
在上一题的基础上需要对横向层级上的重复元素去重,这里使用了
if(i>0 && nums[i] == nums[i-1] && used[i-1] == false)
。其中比较不好理解的是used[i-1] == false.
集合used数组就可以理解了。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> temp = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
backtracking(nums);
return ans;
}
public void backtracking(int[] nums){
if(nums.length == temp.size()){
ans.add(new ArrayList(temp));
return;
}
for(int i=0;i<nums.length;i++){
if(used[i]){
continue;
}
if(i>0 && nums[i] == nums[i-1] && used[i-1] == false){ // 这里的used[i-1] == false或者==true都可以。但是不是说这句话可以不写了。
// 其实,这里是相中剪枝去重的逻辑,used[i-1] == false 结合nums[i] == nums[i-1] 表示这两个数相等,但是前一个被置为false,就说明这是在一层上考虑问题。因为在同一层 除之前使用过的其他的元素一定没使用过,二者其他的元素就是看作在同层。同层内只能取一个数,因此同层内前一个为false 且两个数相等就已经重复了,
// 例如nums=[1,1,1], used[1,1,0] 和[1,0,1]
// [1,1,0] 已经记录过一次了,先有个[1,0,1] 就不要记录了,因为重复
continue;
}
used[i] = true;
temp.add(nums[i]);
backtracking(nums);
used[i] = false;
temp.removeLast();
}
}
}