Day29(回溯)|491. 递增子序列 46. 全排列 47. 全排列 II

491. 递增子序列

  • 题目链接491. 递增子序列
  • 解题思路:可以理解为子集+去重问题,和前面题不一样的是本题不能排序,因为要找递增子序列,去重逻辑也要重新编写。因为元素不能重复使用,所以需要一个startIndex来确定起始位置
  • 解题时间:25+10
  • 代码
class Solution {
    private List<List<Integer>> res=new ArrayList<>(); //保存最后结果的集合
    private List<Integer> path=new ArrayList<>();//保存单次路径集合
    public List<List<Integer>> findSubsequences(int[] nums) {
        back(nums,0);
        return res;
    }
    private void back(int[] nums,int startIndex){
        if(path.size()>1){
            res.add(new ArrayList<>(path));
            //这里还不能return,因为后面叶子节点也可能是可行解
        }
        int[] used=new int[201];
        for(int i=startIndex;i<nums.length;i++){
            if(!path.isEmpty()&&nums[i]<path.get(path.size()-1)||(used[nums[i]+100]==1)){
                continue;
            }
            used[nums[i]+100]=1;
            path.add(nums[i]);
            back(nums,i+1);
            path.remove(path.size()-1);
        }
    }
}

46. 全排列

  • 题目链接46. 全排列
  • 解题思路:本题是无重复数字的全排列问题,每个全排列的长度都应该是总长度,所以不是切割或子集问题,是一种排列问题,需要维护一个used数组来标记原集合中哪些位置的元素已经被用过,构建树形结构来找全排列,树应该是一个完全二叉树的样子,最后结果应该是叶子节点的值
  • 主要难点:与切割和子集不同,因为本题每次都是从0开始搜索,所以不需要维护startIndex,只需要一个used数组即可
  • 解题时间:20+5
  • 代码
class Solution {
    List<List<Integer>> res = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
    boolean[] used;//used数组,表示当前元素是否已经使用过
    public List<List<Integer>> permute(int[] nums) {
        if(nums.length==0){
            return res;
        }
        used=new boolean[nums.length];
        back(nums);
        return res;
    }

    private void back(int[] nums){
        if(path.size()==nums.length){
            //长度为nums长度时为叶子节点,加入结果集合
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]){
                continue;
            }
            used[i]=true;
            path.add(nums[i]);
            back(nums);
            path.removeLast();
            used[i]=false;
        }
    }
}

47. 全排列 II(二刷重点看)

  • 题目链接47. 全排列 II
  • 解题思路:相比上一题加入了去重,要求在一个可重复的序列中找到不重复的全排列,去重应该先排个序
  • 主要难点:同一树枝上可以重复选取,同树层之间不能重复选取
  • 解题时间:20+5
  • 代码
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;
            }
            //如果同⼀树⽀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;//回溯
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值