LeetCode回溯问题合集(general solution)

来源:互联网 发布:不要去淘宝搜血滴子 编辑:程序博客网 时间:2024/05/22 00:33

全排列问题,子集问题,组合和问题都是经典的回溯问题。

对于子集问题,若无重复元素情况下,可以有下面的通用解法,这个解法的关键在于:第一层递归,得出单个元素集合,第二层递归得到两个元素集合,以此类推。

public List<List<Integer>> subsets(int[] nums) {    List<List<Integer>> list = new ArrayList<>();    Arrays.sort(nums);    backtrack(list, new ArrayList<>(), nums, 0);    return list;}private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){    list.add(new ArrayList<>(tempList));    for(int i = start; i < nums.length; i++){        tempList.add(nums[i]);             backtrack(list, tempList, nums, i + 1);        tempList.remove(tempList.size() - 1);      }}
对于带有重复元素的子集问题,也是同样的思路,不同之处在于我们需要过滤掉重复的集合。

首先对数组进行排序,来方便后续过滤重复时候的操作。

接着来分析,按照上边的思路,什么时候会出现重复的集合:对于单个元素集合,我们只需要其出现一次,因此有了skip duplicates的操作;那么,对于两个元素的集合是否也是如此呢?答案是肯定的,因为后续的递归可以看作是没有第一个元素以后的子问题。

public List<List<Integer>> subsetsWithDup(int[] nums) {    List<List<Integer>> list = new ArrayList<>();    Arrays.sort(nums);    backtrack(list, new ArrayList<>(), nums, 0);    return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){    list.add(new ArrayList<>(tempList));    for(int i = start; i < nums.length; i++){        if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates        tempList.add(nums[i]);        backtrack(list, tempList, nums, i + 1);        tempList.remove(tempList.size() - 1);    }} 
对于全排列问题,同理,在没有重复元素时,第一层递归枚举所有长度为1的templist,第二层递归再往上加没有用过的元素。但是。。。那个每次检查是否重复使用的操作,貌似很费时间啊。

public List<List<Integer>> permute(int[] nums) {   List<List<Integer>> list = new ArrayList<>();   // Arrays.sort(nums); // not necessary   backtrack(list, new ArrayList<>(), nums);   return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){   if(tempList.size() == nums.length){      list.add(new ArrayList<>(tempList));   } else{      for(int i = 0; i < nums.length; i++){          if(tempList.contains(nums[i])) continue; // element already exists, skip         tempList.add(nums[i]);         backtrack(list, tempList, nums);         tempList.remove(tempList.size() - 1);      }   }} 
如果有重复元素时,该怎么办呢?

还是先分析,上边那个做法为什么不可行呢。因为数据有重复,不可能使用contains查重了。如何判断重复数据是否加入templist呢?

对于重复数据连在一起的情况,我们只需要其在最后结果中出现一次,如何保证其只出现一次呢,就是在每次递归中保证templist中的数据次序是唯一的。因此需要一个used数组来指示这个次序,只有在前一个数used情况下,后一个重复数才可能接在其后。

public List<List<Integer>> permuteUnique(int[] nums) {    List<List<Integer>> list = new ArrayList<>();    Arrays.sort(nums);    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);    return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){    if(tempList.size() == nums.length){        list.add(new ArrayList<>(tempList));    } else{        for(int i = 0; i < nums.length; i++){            if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;            used[i] = true;             tempList.add(nums[i]);            backtrack(list, tempList, nums, used);            used[i] = false;             tempList.remove(tempList.size() - 1);        }    }}



还有Combination Sum未完待续。。。

续上。

Combination Sum问题是指从某一数组中找出某几个数使其和为定值Target。

第一种情况是可以重复利用元素,这种情况最为简单,套用模板即可解决。

public List<List<Integer>> combinationSum(int[] nums, int target) {    List<List<Integer>> list = new ArrayList<>();    Arrays.sort(nums);    backtrack(list, new ArrayList<>(), nums, target, 0);    return list;}private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){    if(remain < 0) return;    else if(remain == 0) list.add(new ArrayList<>(tempList));    else{         for(int i = start; i < nums.length; i++){            tempList.add(nums[i]);            backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements            tempList.remove(tempList.size() - 1);        }    }}


第二种情况稍微复杂一点,即不可使用重复元素。继续沿用上边的思路,我们只需要在templist加入元素时,保证不加入重复元素即可。为了方便跳过重复元素,也需要首先将原数组进行排序。

public List<List<Integer>> combinationSum2(int[] nums, int target) {    List<List<Integer>> list = new ArrayList<>();    Arrays.sort(nums);    backtrack(list, new ArrayList<>(), nums, target, 0);    return list;    }private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){    if(remain < 0) return;    else if(remain == 0) list.add(new ArrayList<>(tempList));    else{        for(int i = start; i < nums.length; i++){            if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates            tempList.add(nums[i]);            backtrack(list, tempList, nums, remain - nums[i], i + 1); //i+1很关键            tempList.remove(tempList.size() - 1);         }    }} 

另外的几道Combination Sum的题目与上边两种大同小异,都可以沿用这种思路进行求解。



0 0
原创粉丝点击