栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

回溯通用框架解决leetcode排列组合

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

回溯通用框架解决leetcode排列组合

回溯算法通用框架
回溯法框架
    result = []
    backTry(选择列表,路径){
        if 满足结束条件:
            result.add(路径)
            return
        for 选择 in 选择列表:
            做选择
            递归调用
            撤销选择
    }

路径:已经做的选择
选择列表:当前可选列表
总结:

 如果数组中有重复数字,要求输出的结果不能有重复集合,一般解决办法:

        对数组排序,使得相同的数字挨着;

        使用visited数组标记数字是否被访问过。如果nums[i]==nums[i-1]且visited[i-1]=false则nums[i]不再访问,以免重复。

46. 全排列
class Solution {
    List> all; // 用于存储全部符合条件的结果
    boolean[] visited;
    
    public List> permute(int[] nums) {
        all = new ArrayList>();
        visited = new boolean[nums.length];

        List list = new ArrayList(); // 用于存储当前路径
        backTry(list, nums);
        
        return all;
    }

    public void backTry(List list, int[] nums){
        if(list.size() == nums.length){ // 满足结束条件:选了nums中所有的数字
            all.add(new ArrayList(list));
            return;
        }
        for(int i=0;i 
47. 全排列 II 
class Solution {
    // nums中有重复数字,要求输出结果不能有重复    

    List> all; // 用于存储全部符合条件的结果
    boolean[] visited;
    
    public List> permuteUnique(int[] nums) {
        all = new ArrayList>();
        visited = new boolean[nums.length];
        Arrays.sort(nums);
        List list = new ArrayList(); // 用于存储当前路径
        backTry(list, nums);
        
        return all;
    }

    public void backTry(List list, int[] nums){
        if(list.size() == nums.length){ // 满足结束条件:选了nums中所有的数字
            all.add(new ArrayList(list));
            return;
        }
        for(int i=0;i0 && nums[i]==nums[i-1] && !visited[i-1]){continue;}
            if(visited[i]==false){ // 只有没有访问过,才做选择    
                // 做选择
                list.add(nums[i]);
                visited[i] = true;
                // 递归调用
                backTry(list, nums);
                // 撤销选择
                list.remove(list.size()-1);
                visited[i] = false;
            }
        }
    }
}

78. 子集

数组nums中不包含重复元素,要求输出结果是没有重复子集的  nums = [1,2,3]

不能有重复子集:那么就是当前数字选择完后,选择后面的数字,这样就会避免出现[1,2]  [2,1]这样的重复情况

class Solution {
    // 幂集:从空到数组长度所有的子集

    List> all; // 存放所有的路径
    boolean[] visited;
    public List> subsets(int[] nums) {
        all = new ArrayList>();
        visited = new boolean[nums.length];

        List list = new ArrayList(); // 存放当前选择路径
        for(int i = 0;i < nums.length; i++){
            backTry(list, nums, i); // 从空路径、长度为0开始选择
        }
        return all;
    }

    public void backTry(List list, int[] nums, int len){
        if(list.size() == len){ // 结束条件:如果当前选择的数字个数等于幂集的长度
            all.add(new ArrayList(list));
            return;
        }
        for(int i = 0;i < nums.length; i++){
            if(visited[i] == false){ // 只有当没有被访问过才选择
                // 做选择
                list.add(nums[i]);
                visited[i] = true;
                // 递归调用
                backTry(list, nums, len);
                // 撤回选择
                list.remove(list.size()-1);
                visited[i] = false;
            }
        }
    }
}

怎么在回溯的过程中对子集去重呢??上一版代码可以看出,2选择之后又去选择1,但其实是每个数字都选择他后面的数字就不会出现有[1,2] 和 [2,1]这样的情形了。

class Solution {
    // 幂集:从空到数组长度所有的子集

    List> all; // 存放所有的路径
    public List> subsets(int[] nums) {
        all = new ArrayList>();

        List list = new ArrayList(); // 存放当前选择路径
        for(int i = 0;i <= nums.length; i++){
            backTry(list, nums, 0, i); // 从空路径、位置0,长度为i开始选择
        }
        return all;
    }

    public void backTry(List list, int[] nums, int idx, int len){
        if(list.size() == len){ // 结束条件:如果当前选择的数字个数等于幂集的长度
            all.add(new ArrayList(list));
            return;
        }
        for(int i = idx;i < nums.length; i++){
            // 做选择
            list.add(nums[i]);
            // 递归调用,为避免重复,每次选择该数字的下一个数字
            backTry(list, nums, i+1, len);
            // 撤回选择
            list.remove(list.size()-1);
        }
    }
}
90. 子集 II
class Solution {
    // 上一题的数组中不包含重复元素,本题nums中有重复元素,但要求不变
    // 怎么避免[2] [2] [1,2] [1,2] 这样的子集出现?可以发现:如果第一个2 被访问过,第二个2与前一个2相同,且前一个2北方问过,那么第二个2就不能被访问,所以去重,我们可以借助visited数组


    List> all; // 存放所有的幂集
    boolean[] visited;
    public List> subsetsWithDup(int[] nums) {
        all = new ArrayList>();
        visited = new boolean[nums.length];
        List list = new ArrayList(); // 存放当前选择路径
        Arrays.sort(nums);
        for(int i=0;i<=nums.length;i++){ // 幂集的长度从0到数组长度
            backTry(list, nums, i, 0); // 仍旧从空选择、幂集长度为i,位置0开始选择
        }
        return all;
    }
    public void backTry(List list, int[] nums, int len, int idx){
        if(list.size() == len){ // 结束条件:仍旧是list中数字的个数等于幂集的长度
            all.add(new ArrayList(list));
            return;
        }
        for(int i=idx;i0 && nums[i] == nums[i-1] && !visited[i-1]){continue;}
            // 做选择
            list.add(nums[i]);
            visited[i] = true;
            // 递归调用
            backTry(list, nums, len, i+1);
            // 撤回选择
            list.remove(list.size()-1);
            visited[i] = false;
        }
    }
}
77. 组合
class Solution {

    List> all; // 存储所有的选择
    public List> combine(int n, int k) {
        all = new ArrayList>();
        List list = new ArrayList(); // 存储当前选择的路径
        backTry(list, n, 1, k);// 选择从1到n
        
        return all;
    }
    public void backTry(List list, int n, int idx, int k){
        if(list.size() == k){ // 结束条件
            all.add(new ArrayList(list));
            return;
        }
        for(int i=idx;i<=n;i++){ // 遍历所有的选择
            // 做选择
            list.add(i);
            // 递归调用
            backTry(list, n, i+1, k);
            // 撤回选择
            list.remove(list.size()-1);
        }
    }
}
39. 组合总和
class Solution {
    // 数组无重复元素,同一个数字可以重复选择,选出组合和为target的所有组合
    
    List> all;
    public List> combinationSum(int[] candidates, int target) {
        all = new ArrayList>();
        List list = new ArrayList();
        backTry(list, candidates, target, 0); // 从位置0,空list开始选择
        return all;
    }

    public void backTry(List list, int[] candidates, int target, int idx){
        if(target == 0){ // 结束条件:如果目标和等于0了,说明已经选择数字和等于target了
            all.add(new ArrayList(list));
            return;
        }
        for(int i=idx; i=0){ // 是否满足选择条件
                // 做选择
                list.add(candidates[i]);
                target -= candidates[i];
                // 递归调用
                backTry(list, candidates, target, i);
                // 撤回选择
                list.remove(list.size()-1);
                target += candidates[i];
            }
        }
    }
}
40. 组合总和 II
class Solution {
    // 数组有重复元素,同一个数字只能选择一次,选出组合和为target的所有组合
    
    List> all;
    boolean[] visited;
    public List> combinationSum2(int[] candidates, int target) {
        all = new ArrayList>();
        visited = new boolean[candidates.length];
        List list = new ArrayList();

        Arrays.sort(candidates);

        backTry(list, candidates, target, 0); // 从位置0,空list开始选择
        return all;
    }

    public void backTry(List list, int[] candidates, int target, int idx){
        if(target == 0){ // 结束条件:如果目标和等于0了,说明已经选择数字和等于target了
            all.add(new ArrayList(list));
            return;
        }
        for(int i=idx; itarget || visited[i] || i>0 && candidates[i]==candidates[i-1] && !visited[i-1]){continue;}

            // 做选择
            list.add(candidates[i]);
            target -= candidates[i];
            visited[i] = true;
            // 递归调用
            backTry(list, candidates, target, i+1);
            // 撤回选择
            list.remove(list.size()-1);
            target += candidates[i];
            visited[i] = false;
            
        }
    }
}
216. 组合总和 III
class Solution {
    // 从数字1~9中选择k个和为n的组合,每个数字最多使用一次

    List> all;
    public List> combinationSum3(int k, int n) {
        all = new ArrayList>();
        List list = new ArrayList();

        backTry(list, 1, n, k); // 从数字1,空list开始选择
        return all;
    }

    public void backTry(List list, int idx, int n, int k){
        if(n == 0 && list.size() == k){ // 结束条件:如果目标和等于0了,说明已经选择数字和等于n了
            all.add(new ArrayList(list));
            return;
        }
        for(int i=idx;i<=9;i++){
            // 是否满足选择条件: 如果该数字大于n
            if(i>n){continue;}
            // 做选择
            list.add(i);
            n -= i;
            // 递归调用
            backTry(list, i+1, n, k);
            // 撤回选择
            list.remove(list.size()-1);
            n += i;
            
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939710.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号