YUAN 2019-06-26
使用回溯算法解题的一般步骤:
LeetCode 使用回溯算法的题目主要有 36 题,代表性有 N Queens(51,52), SubSets(78), Permutation(46(distinct), 47(with duplicates)), Combination, Combination Sum.
SubSets:
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,3], a solution is:
[[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[]]
SubSets 这道题要求 print 出定长 int array 中所有子集合,使用回溯算法遍历定长 array,然后回溯来选择出所有可能的组合.
针对 backTrack 时间复杂度的分析直接复习结果集的大小有效
Runtime: O(2^n), Space: O(n)
Solution:
public List<List<Integer>> subsets(int[] nums) { if(nums == null || nums.length == 0) return null;` List<List<Integer>> res = new ArrayList<List<Integer>>(); helper(nums, res, 0, new ArrayList<>()); return res; } public void helper(int[] nums, List<List<Integer>> res, int index, List<Integer> temp) { //考虑空子集的情况 res.add(new ArrayList<>(temp)); for(int i = index; i < nums.length; i++) { //每次加下一个index的element temp.add(nums[i]); //深度优先到下一层 helper(nums, res, i + 1, temp); //backTracking temp.remove(temp.size() - 1); } }
针对如果输入数组有重复的元素,但是要求输出的结果没有重复,可以一些去重的方法并注意防止溢出
If nums = [1,2,2], a solution is [[2],[1],[1,2,2],[2,2],[1,2],[]]
Solution:
public List<List<Integer>> subsetsWithDup(int[] nums) { if(nums == null || nums.length == 0) return null; List<List<Integer>> res = new ArrayList<List<Integer>>(); //对input数组进行排序,准备为数组进行去重 Arrays.sort(nums); helper(nums, res, 0, new ArrayList<>()); return res; } public void helper(int[] nums, List<List<Integer>> res, int index, List<Integer> temp) { res.add(new ArrayList<>(temp)); for(int i = index; i < nums.length; i++) { //去重复,并且防止溢出 if(i != index && nums[i] == nums[i - 1]) continue; temp.add(nums[i]); helper(nums, res, i + 1, temp); temp.remove(temp.size() - 1); } }
Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7] and target 7, A solution set is:
[[7],[2, 2, 3]]
这道题要求given一个定长数组,要求找到所有子数组集合以至于子数组所有数的和等于目标数,可以运用回溯算法,区间为given的定长数组
Solution:
同样是用回溯的方法,但是针对数组中子数可以重复使用time:O(2^n), space: O(n)
public List<List<Integer>> combinationSum(int[] candidates, int target) { if(candidates == null || candidates.length == 0) return null; List<List<Integer>> res = new ArrayList<List<Integer>>(); helper(candidates, target, res, new ArrayList<Integer>(), 0); return res; } public void helper(int[] candidates, int target, List<List<Integer>>res, List<Integer> temp, int index) { if(target < 0) return; else if(taget == 0) res.add(new ArrayList<Integer>(temp)); else { for(int i = index; i < candidats.length; i++) { temp.add(candidates[i]); //DFS dive into next level, 因为需要重复使用所以递归调用i 没有变化 helper(candidates, target - candidates[i], res, temp, i); temp.remove(temp.size() - 1); } }
变种,针对CombinationSum 数组中子数只可以被使用一次:
时间复杂度仍然为O(2^n), 空间复杂度为O(n)
public List<List<Integer>> combinationSum(int[] candidates, int target) { if(candidates == null || candidates.length == 0) return null; List<List<Integer>> res = new ArrayList<List<Integer>>(); helper(candidates, target, res, new ArrayList<Integer>(), 0); return res; } public void helper(int[] candidates, int target, List<List<Integer>>res, List<Integer> temp, int index) { if(target < 0) return; else if(taget == 0) res.add(new ArrayList<Integer>(temp)); else { for(int i = index; i < candidats.length; i++) { //进行防溢出和去重 if(i != index && candidates[i] == candidates[i - 1]) continue; temp.add(candidates[i]); //DFS dive into next level, 因为需要重复使用所以递归调用i 没有变化 helper(candidates, target - candidates[i], res, temp, i); temp.remove(temp.size() - 1); } }