回溯法解算法题-leetcode中的组合排列求子集问题

回溯算法的模板

List<Object> list = new ArrayList<>();
public void backtrack(路径,选择列表){
    //设置结束条件
    if 满足结束条件{
        lista.dd(路径);
        return;
    }
    //候选节点的选择
    for(int i=start;i<选择列表的长度;i++){
        //做选择  是否要将当前节点添加到list中
        list.add(nums[i]);
        //开启下一轮的遍历
        backtrack(路径,选择列表);
        //撤销选择
        list.remove(list.size()-1);
  
    }
    
}

下面做一些leetcode典型的使用回溯解决的算法题。

leetcode 46. 全排列

题目描述:给定一个 没有重复 数字的序列,返回其所有可能的全排列。

class Solution {
    public List<List<Integer>> list;  //保存最终结果
    public List<Integer> cur;  //保存当前路径
    public List<List<Integer>> permute(int[] nums) {
        list = new ArrayList<>();
        cur = new ArrayList<>();
        boolean[] flag = new boolean[nums.length];  //判断当前元素是否已经被访问过了
        backtrack(nums,list,cur,flag);
        return list;
    }

    public void backtrack(int[] nums,List<List<Integer>> list,List<Integer> cur,boolean[] flag){
        //设置结束条件
        if(cur.size()==nums.length){
            list.add(new ArrayList(cur));
            return;
        }
        //候选节点的选择
        for(int i=0;i<nums.length;i++){
            if(flag[i]==false){
                //如果当前元素没有被访问过
                cur.add(nums[i]);
                flag[i]=true;
                //进行下一步的遍历
                backtrack(nums,list,cur,flag);
                //撤销操作
                flag[i]=false;
                cur.remove(cur.size()-1);
            }       
        }
    }
}

leetcode 47. 全排列 II

题目描述:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

这道题和上面一道题的区别就是这道题中nums包含重复数字,只要在将cur添加在list里面的时候,判断一下是否list中包含了cur。

class Solution {
    public List<List<Integer>> list;  //保存最终结果
    public List<Integer> cur;  //保存当前路径

    public List<List<Integer>> permuteUnique(int[] nums) {
        list = new ArrayList<>();
        cur = new ArrayList<>();
        boolean[] flag = new boolean[nums.length];  //判断当前元素是否已经被访问过了
        backtrack(nums,list,cur,flag);
        return list;
    }
    public void backtrack(int[] nums,List<List<Integer>> list,List<Integer> cur,boolean[] flag){
        //设置结束条件
        if(cur.size()==nums.length){
            if(!list.contains(cur)){
                //确保不重复
                list.add(new ArrayList(cur));
            }
            return;
        }
        //候选节点的选择
        for(int i=0;i<nums.length;i++){
            if(flag[i]==false){
                //如果当前元素没有被访问过
                cur.add(nums[i]);
                flag[i]=true;
                //进行下一步的遍历
                backtrack(nums,list,cur,flag);
                //撤销操作
                flag[i]=false;
                cur.remove(cur.size()-1);
            }       
        }
    }
}

leetcode 78. 子集

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

class Solution {
    public List<List<Integer>> list;  //最终结果
    public List<Integer> cur;  //当前子集列表
    public List<List<Integer>> subsets(int[] nums) {
        list = new ArrayList<>();
        cur = new ArrayList<>();
        list.add(cur); //首先将空集加入
        dfs(0,nums,list,cur);
        return list;
    }
    public void dfs(int index,int[] nums,List<List<Integer>> list,List<Integer> cur){
        //设置终止条件
        if(index==nums.length){
            return;
        }
        //候选节点的选择
        for(int i=index;i<nums.length;i++){
            //进行选择
            cur.add(nums[i]);
            list.add(new ArrayList(cur));
            dfs(i+1,nums,list,cur);
            //撤销选择
            cur.remove(cur.size()-1);
        }
    }
}

leetcode 90. 子集 II

题目描述:

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。

这道题比上一道题多了去重。

class Solution {
    public List<List<Integer>> list;  //最终结果
    public List<Integer> cur;  //当前子集列表
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //首先对nums进行排序
        Arrays.sort(nums);
        List<List<Integer>> list = new ArrayList<>();
        List<Integer> cur = new ArrayList<>();
        list.add(cur);  //首先将空集加入
        dfs(nums,0,list,cur);
        return list;
    }
    public void dfs(int[] nums,int start,List<List<Integer>> list,List<Integer> cur){
        //设立结束条件
        if(start==nums.length){
            return;
        }
        for(int i=start;i<nums.length;i++){
            //去重
            if(i>start && nums[i]==nums[i-1]) continue;
            //做出选择
            cur.add(nums[i]);
            list.add(new ArrayList(cur));
            dfs(nums,i+1,list,cur);
            //撤销选择
            cur.remove(cur.size()-1);
        }
    }
}

leetcode77. 组合

题目描述:给定两个整数 n 和 k,返回 1 ... 中所有可能的 k 个数的组合。

这道题其实类似于求子集,不过是限定长度的子集。

class Solution {

    public List<List<Integer>> list; //最终结果
    public List<Integer> cur;  //当前子集列表
    public List<List<Integer>> combine(int n, int k) {
        list = new ArrayList<>();
        cur = new ArrayList<>();
        dfs(n,k,1,list,cur);
        return list;
    }

    public void dfs(int n,int k,int start,List<List<Integer>> list,List<Integer> cur){
        //结束条件
        if(k==cur.size()){
            list.add(new ArrayList(cur));
            return;
        }

        //候选节点
        for(int i=start;i<=n;i++){
            //做出选择
            cur.add(i);
            dfs(n,k,i+1,list,cur);
            //撤销选择
            cur.remove(cur.size()-1);
        }
    }
}

leetcode 39. 组合总和

题目描述:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。

class Solution {
    public List<List<Integer>> list;  //保存最终结果
    public List<Integer> cur;  //当前列表
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        list = new ArrayList<>();
        cur = new ArrayList<>();
        int cursum = 0;  //表示当前列表中的和
        backtrack(candidates,target,list,cur,cursum);
        return list;
    }

    public void backtrack(int[] candidates, int target,List<List<Integer>> list,List<Integer> cur,int cursum){
        //设置结束条件
        if(cursum>=target){
            if(cursum==target){
                //对cur进行一个排序
                List<Integer> temp = new ArrayList<>(cur);
                Collections.sort(temp);
                //题目要求解集中不能包含重复的组合 [2,2,3]和[3,2,2]算重复
                if(!list.contains(temp)){
                    list.add(temp);
                } 
            }
            return;
        }
        //候选节点的选择
        for(int i=0;i<candidates.length;i++){  
            //做出选择          
            cur.add(candidates[i]);
            cursum+=candidates[i];
            backtrack(candidates,target,list,cur,cursum);
            //撤销选择
            cursum-=candidates[i];
            cur.remove(cur.size()-1); 
        }
    }
}

leetcode 17. 电话号码的字母组合

 以下为Java代码实现:

class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> list = new ArrayList<>();
        if(digits.length()==0){
            return list;
        }
        String[] strs = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};		
		list.add("");
		for(int i=0;i<digits.length();i++) {
			List<String> temp = new ArrayList<String>();
			for(int j=0;j<strs[digits.charAt(i)-'2'].length();j++) {
				for(int k=0;k<list.size();k++) {
					temp.add(list.get(k)+strs[digits.charAt(i)-'2'].charAt(j));
				}
			}
			list = temp;
		}
		return list;
    }   
}

还有非常多类似的题,学着做一下总结,回溯太难了,任重道远。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值