【回溯算法】|代码随想录算法训练营第22天|491.递增子序列、 46.全排列、47.全排列 II、【总结】

刷题神器

代码随想录

往期回顾

>【回溯算法】|代码随想录算法训练营第21天|93. 复原 IP 地址、 78. 子集、90.子集II

题目

491.递增子序列

题目:题目链接
文章:文章讲解
视频:视频讲解

  • 学后思路

    递增子序列,注意无法进行排序,排序后影响结果集,去重方式依然是树层去重,但是去重得逻辑有所变化

解法1:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTracking(nums, 0);
        return result;
    }
    private void backTracking(int[] nums, int startIndex){
        if(path.size() >= 2)
                result.add(new ArrayList<>(path));            
        HashSet<Integer> hs = new HashSet<>();
        for(int i = startIndex; i < nums.length; i++){
            if(!path.isEmpty() && path.get(path.size() -1 ) > nums[i] || hs.contains(nums[i]))
                continue;
            hs.add(nums[i]);
            path.add(nums[i]);
            backTracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

  • 题目总结
    • 本地也是在节点收获结果,但是题目控制了结果长度
    • 定义了set去重,在一层间进行去重
    • 判断递增逻辑,加入节点是否大于path的最右边节点值

46.全排列

题目:题目链接
文章:文章讲解
视频:视频讲解

  • 学后思路
    全排列要注意和组合的区别,元素相同,顺序不同,算两个排列,结题思路和回溯三部曲类似

解法一:

class Solution {

    List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        if (nums.length == 0){
            return result;
        }
        used = new boolean[nums.length];
        backtracking(nums);
        return result;
    }

    private void backtracking (int[] nums){
        if (path.size() == nums.length){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++){
            if (used[i]){
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            backtracking(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

  • 题目总结
    • 注意每次循环都是从0开始
    • 注意判断是否使用的精妙判断,使用used,然后continue
    • 收获结果全部都在叶子节点,判断逻辑就是path.size()=numbers.size()

47.全排列 II

题目:题目链接
文章:文章讲解
视频:视频讲解

  • 学后思路
    全排列2的结题思路和全排列类似,因为有了重复元素的出现,所以在树层上要进行去重

解法一:

class Solution {
    //存放结果
    List<List<Integer>> result = new ArrayList<>();
    //暂存结果
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] used = new boolean[nums.length];
        Arrays.fill(used, false);
        Arrays.sort(nums);
        backTrack(nums, used);
        return result;
    }

    private void backTrack(int[] nums, boolean[] used) {
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            // used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过
            // used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过
            // 如果同⼀树层nums[i - 1]使⽤过则直接跳过
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            if(used[i] == true) continue;
            //如果同⼀树⽀nums[i]没使⽤过开始处理
            if (used[i] == false) {
                used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树枝重复使用
                path.add(nums[i]);
                backTrack(nums, used);
                path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复
                used[i] = false;//回溯
            }
        }
    }
}

  • 题目总结
    • 题目排列和去重的一个组合体,要注意去重和跳过的逻辑
    • 因为重复元素要去重,所以数组要优先进行排列

总结

回溯算法主要解决以下问题

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

解题思路主要是回溯三部曲

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

重点掌握组合、排列、切割、子集 中间有相似的解法,在去重上有相似的思路

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值