回溯算法的应用
491.递增子序列
注意: 此题与求最长递增子序列不同,求最长只需要找出一种, 用动态规划方法即可 本题是要求所有的自增子序列, 不到末尾的也算
- 回溯法: 当遍历到的值比路径集合中的最后一个值大时, 就收集当前值到集合中
- 同时, 每当集合中元素大于2, 就将路径集合加入到结果集中
代码:
class Solution {
// 排序+横向去重 == 每层一个哈希表去重
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return ans;
}
public void backtracking(int[] nums, int start) {
if(start == nums.length) return;
HashSet<Integer> set = new HashSet<>();
for(int i=start; i<nums.length; i++){
if(list.size() > 0 && nums[i] < list.getLast()){
continue;
}
// 因为不能排序, 所以不能使用该方法进行同一层横向去重
// if(i > start && nums[i] == nums[i-1]) continue;
if(set.contains(nums[i])) continue;
list.add(nums[i]);
set.add(nums[i]);
if(list.size() > 1){
ans.add(new ArrayList<>(list));
}
backtracking(nums, i+1);
list.removeLast();
}
}
}
注意,需要横向去重---两种方法
- 允许排序: 排序 + nums[i] 与nums[i-1] 比较去重
- 不允许排序: 则每层一个哈希表去重
46.全排列
- 全排列的题目, 每层的起点都是从0开始, 所以不需要传入下一层的起点start
- 由于每层都会出现所有的元素, 所以需要路径上的纵向去重
- 若没有重复元素, 则直接判断路径集合list中是否存在重复元素即可
- 若有重复元素, 此时需要横向去重, 另外需要判断一条路径上出现过的元素不能再出现, 因为有重复元素, 不能通过哈希表或者list去重, 需要位置标记数组或者hashmap去重
代码:
class Solution {
// 区别: 组合有序, 排列无序
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
HashSet<Integer> set = new HashSet<>();
public List<List<Integer>> permute(int[] nums) {
backtracking(nums);
return ans;
}
public void backtracking(int[] nums) {
if(list.size() == nums.length){
ans.add(new ArrayList<>(list));
return;
}
for(int i=0; i<nums.length; i++){
if(set.contains(nums[i])) continue;
list.add(nums[i]);
set.add(nums[i]);
backtracking(nums);
list.removeLast();
set.remove(nums[i]);
}
}
}
47.全排列II
本题与上一题的区别是, 本题中有重复元素
- 需要横向去重
- 纵向路径去重: 不能依靠于全局哈希表和list集合去重, 需要依靠位置标记数组或者hashmap
代码:
class Solution {
List<List<Integer>> ans = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
HashMap<Integer, Integer> map = new HashMap<>();
for(int i:nums){
map.put(i, map.getOrDefault(i, 0) + 1);
}
backtracking(nums, map);
return ans;
}
public void backtracking(int[] nums, HashMap<Integer, Integer> map) {
if(list.size() == nums.length){
ans.add(new ArrayList<>(list));
return;
}
for(int i=0; i<nums.length; i++){
// 路径去重
if(map.get(nums[i]) < 1) continue;
// 横向去重
if(i > 0 && nums[i] == nums[i-1]) continue;
list.add(nums[i]);
map.put(nums[i], map.get(nums[i]) - 1);
backtracking(nums, map);
list.removeLast();
map.put(nums[i], map.get(nums[i]) + 1);
}
}
}