面试必考06——回溯法算法题总结

注意组合类和子集类问题都不需要考虑顺序,排列类问题需要考虑顺序。

组合和子集类问题:

  1. 求总和等于target,就要判断target==0;
  2. i=start,结果中不能含有重复元素,i+1,结果可以含有重复元素,i(子集问题不能含重复元素)
  3. 如果给定的数中有重复元素,需要Arrays.sort();判断if (i > start && nums[i] == nums[i - 1]) {continue;}

全排列类问题:

  1. 判断list.size()==nums.length;
  2. i=0,结果肯定不能含有重复元素
  3. 没有重复元素用contains
  4. 如果有给定的数中有重复元素,需要Arrays.sort();需要used数组;判断if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) {continue;}
1.组合总和1(数组无重复元素,数字可以被重复选取)

无重复元素的数组和一个目标数 target

public class T01_CombinationSum {
    //数组中无重复元素

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (candidates == null || candidates.length == 0) {
            return res;
        }
        helper(res, new ArrayList<Integer>(), candidates, target, 0);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int start) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        if (target < 0) {
            return;
        }
        for (int i = start; i < candidates.length; i++) {
            list.add(candidates[i]);
            helper(res, list, candidates, target - candidates[i], i);//可以选取重复元素
//            helper(res, list, candidates, target - candidates[i], i + 1);//不可以选取重复元素
            list.remove(list.size() - 1);
        }
    }
}
2.组合总和2(数组可能有重复元素,数字不可以被重复选取)

在这里插入图片描述

public class T02_CombinationSum2 {
    //数组中不知有无重复元素,数字不可被重复选取
    //这个代码同样适用于无重复元素,数字可被重复选取的,只需要将倒数第二行的i+1变成i即可
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (candidates == null || candidates.length == 0) {
            return res;
        }
        Arrays.sort(candidates);//不同
        helper(res, new ArrayList<Integer>(), candidates, target, 0);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int start) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        if (target < 0) {
            return;
        }
        for (int i = start; i < candidates.length; i++) {
            if (i > start && candidates[i] == candidates[i - 1]) {//不同
                continue;
            }
            list.add(candidates[i]);
            helper(res, list, candidates, target - candidates[i], i + 1);//不可以选取重复元素
            list.remove(list.size() - 1);
        }
    }
}
3.子集1(数组无重复元素)

子集类问题和组合总和类问题的区别在于少了target的判断,并且同一个元素不会选取两次在这里插入图片描述

public class T03_Subsets {
    public List<List<Integer>> subsets(int[] nums) {
        //不包含重复元素
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        helper(res, new ArrayList<>(), nums, 0);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int start) {
        res.add(new ArrayList<>(list));
        for (int i = start; i < nums.length; i++) {
            list.add(nums[i]);
            helper(res, list, nums, i + 1);//子集,同一个元素不会被选两次
            list.remove(list.size() - 1);
        }
    }
}
4.子集2(数组可能有重复元素)

在这里插入图片描述

public class T04_Subsets2 {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //可能包含重复元素,这个代码也适用于无重复元素的
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        Arrays.sort(nums);
        helper(res, new ArrayList<>(), nums, 0);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int start) {
        res.add(new ArrayList<>(list));
        for (int i = start; i < nums.length; i++) {
            if (i > start && nums[i] == nums[i - 1]) {
                continue;
            }
            list.add(nums[i]);
            helper(res, list, nums, i + 1);//子集,同一个元素不会被选两次
            list.remove(list.size() - 1);
        }
    }
}
5.组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。(和子集问题很像,区别是限制了长度k,同样也是不可以选取重复元素)

public class T05_Combine {
    public List<List<Integer>> combine(int n, int k) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (n < 1 || k > n) {
            return res;
        }
        helper(res, new ArrayList<>(), n, k, 1);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int n, int k, int start) {
        if (list.size() == k) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = start; i <= n; i++) {
            list.add(i);
            helper(res, list, n, k, i + 1);//组合,肯定也是不能选取重复元素的
            list.remove(list.size() - 1);
        }
    }
}
6.全排列1(没有重复元素)

给定一个没有重复数字的序列,返回其所有可能的全排列。
(没有重复元素那就用contains方法就可以)
(全排列类问题总是从0开始遍历,不需要start标记,需要判断长度)

public class T06_Permute {
    public List<List<Integer>> permute(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        helper(res, new ArrayList<>(), nums);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] nums) {
        if (list.size() == nums.length) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (list.contains(nums[i])) {//不同
                continue;
            }
            list.add(nums[i]);
            helper(res, list, nums);
            list.remove(list.size() - 1);
        }
    }
}
7.全排列2(可能有重复元素)

给定一个可能包含重复数字的序列,返回所有不重复的全排列。(也适用于无重复元素的)
(有重复元素,需要用一个used数组判断元素是否被使用过)

public class T07_Permute2 {
    public List<List<Integer>> permute2(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        Arrays.sort(nums);
        helper(res, new ArrayList<>(), nums, new boolean[nums.length]);
        return res;
    }

    private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used) {
        if (list.size() == nums.length) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            //used数组用来区分数字是否被使用过
            if (used[i]) {//不同
                continue;
            }
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) {//不同
                continue;
            }
            used[i] = true;//不同
            list.add(nums[i]);
            helper(res, list, nums, used);
            list.remove(list.size() - 1);
            used[i] = false;//不同
        }
    }
}
8.第k个排列

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
给定 n 和 k,返回第 k 个排列。
和全排列问题很相似,区别是多加了size等于k时停止,并且把结果拼接成字符串

public class T08_getKthPermutation {
    public String getPermutation(int n, int k) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
        helper(res, new ArrayList<>(), n, k);

        StringBuilder str = new StringBuilder();//最后将list拼接成字符串
        ArrayList<Integer> l = res.get(k - 1);
        for (int i = 0; i < l.size(); i++) {
            str.append(l.get(i));
        }
        return str.toString();
    }

    private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, int n, int k) {
        if (res.size() == k) {
            return;
        }
        if (list.size() == n) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (list.contains(i)) {
                continue;
            }
            list.add(i);
            helper(res, list, n, k);
            list.remove(list.size() - 1);
        }
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值