夸父追日:第七章 回溯算法part04

今日收获:递增子序列,全排列,全排列Ⅱ,重新安排行程,N皇后,解数独

1. 递增子序列

题目链接:491. 非递减子序列 - 力扣(LeetCode)

思路:

        1. 不能选同一层相同元素,可以选同一个树枝上不同位置的相同元素。之前这样要求的去重逻辑是对原数组排序后,用同样大小的数组标记。本题不能对数组排序,所以要用哈希法去重。

        2. 搜集树中所有的子节点

        3. 如果path不为空且当前节点小于上一个路径节点(不满足递增子序列的条件),或者本元素已经使用过了(同一层元素重复了),则跳过本节点(即本次循环)。

方法:

class Solution {
    List<List<Integer>> result=new ArrayList<>();
    List<Integer> path=new ArrayList<>();

    public List<List<Integer>> findSubsequences(int[] nums) {
        back(nums,0); 
        return result;
    }

    public void back(int[] nums,int start){
        if (path.size()>=2){  // 至少有两个元素,取所有子节点
            result.add(new ArrayList<>(path));
        }

        HashSet<Integer> used=new HashSet<>();

        for (int i=start;i<nums.length;i++){
            int len=path.size();

            // 确保当前选择的元素大于等于路径的上一个元素并去除重复
            if (len>0&&nums[i]<path.get(len-1)||used.contains(nums[i])){  
                continue;
            }

            path.add(nums[i]);
            used.add(nums[i]);
            back(nums,i+1);
            path.removeLast();
        }
    }
}

总结:

        1. 当不能对数组排序且需要去重同一层节点时,需要用哈希法去重。在用数组去重时,每一个元素都有唯一的位置标识,所以每个回溯函数共享使用数组。但是用哈希法时每个回溯函数都要新建一个哈希集合,确保去重只在本层生效;不能共享,否则对每一条路径上的元素也去重了。

        2. 当前节点不满足特定条件就跳过本次循环,continue下一个更乖!

2. 全排列

题目链接:46. 全排列 - 力扣(LeetCode)

思路:使用used数据记录选取过的元素,每一次都要从头遍历数组选取未使用过的元素。终止条件是所有的元素都用完了

方法:

class Solution {
    List<Integer> path=new ArrayList<>();
    List<List<Integer>> result=new ArrayList<>(); 

    public List<List<Integer>> permute(int[] nums) {
        int[] used=new int[nums.length];
        for (int i=0;i<nums.length;i++){
            used[i]=0;
        }
        back(nums,used);
        return result;
    }

    public void back(int[] nums,int[] used){
        int len=nums.length;

        // 所有节点都用完了
        if (path.size()==len){
            result.add(new ArrayList<>(path));
            return;
        }

        for (int i=0;i<len;i++){
            if (used[i]==1){
                continue;
            }
            path.add(nums[i]);
            used[i]=1;
            back(nums,used);
            path.removeLast();
            used[i]=0;
        }
    } 
}

3. 全排列Ⅱ

题目链接:47. 全排列 II - 力扣(LeetCode)

思路:在上一题的基础上添加同一层去重的逻辑

方法:

class Solution {
    List<Integer> path=new ArrayList<>();
    List<List<Integer>> result=new ArrayList<>();

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

    public void back(int[] nums,int[] used){
        int len=nums.length;

        // 所有节点都用完了
        if (path.size()==len){
            result.add(new ArrayList<>(path));
            return;
        }

        for (int i=0;i<len;i++){
            // 同一层使用过
            if (i>0&&nums[i]==nums[i-1]&&used[i-1]==0){
                continue;
            }
            if (used[i]==0){  // 同一个树枝上没有使用过
                path.add(nums[i]);
                used[i]=1;
                back(nums,used);
                path.removeLast();
                used[i]=0;
            }
        }
    } 
}

总结:需要记忆同一层去重的固定写法

if (i>0&&nums[i]==nums[i-1]&&used[i-1]==0){
                continue;
            }

4. 重新安排行程(一刷了解思路)

题目链接:332. 重新安排行程 - 力扣(LeetCode)

二刷一定可以啃下来!

5. N皇后(一刷了解思路)

题目链接:51. N 皇后 - 力扣(LeetCode)

二刷一定可以啃下来!

6. 解数独(一刷了解思路)

题目链接:37. 解数独 - 力扣(LeetCode)

二刷一定可以啃下来!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值