代码随想录-2023/07/26

143 阅读1分钟

回溯算法的应用

491.递增子序列

注意: 此题与求最长递增子序列不同,求最长只需要找出一种, 用动态规划方法即可 本题是要求所有的自增子序列, 不到末尾的也算

  1. 回溯法: 当遍历到的值比路径集合中的最后一个值大时, 就收集当前值到集合中
  2. 同时, 每当集合中元素大于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();
           
        }
    }
}
注意,需要横向去重---两种方法
  1. 允许排序: 排序 + nums[i] 与nums[i-1] 比较去重
  2. 不允许排序: 则每层一个哈希表去重

46.全排列

  1. 全排列的题目, 每层的起点都是从0开始, 所以不需要传入下一层的起点start
  2. 由于每层都会出现所有的元素, 所以需要路径上的纵向去重
  3. 若没有重复元素, 则直接判断路径集合list中是否存在重复元素即可
  4. 若有重复元素, 此时需要横向去重, 另外需要判断一条路径上出现过的元素不能再出现, 因为有重复元素, 不能通过哈希表或者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

本题与上一题的区别是, 本题中有重复元素

  1. 需要横向去重
  2. 纵向路径去重: 不能依靠于全局哈希表和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);
        }
    }
}