数字组合问题:Combination,CombinationSum,CombinationSum2,CombinationSum3
Combination问题描述:给定n和k,找出1-n之间所有k个数的组合,例如:n=3,k=2,返回
[[1,2] [1,3] [2,3]]
算法分析:利用递归。递归边界就是curr.size()==k。
1 public List<List<Integer>> combine(int n, int k) 2 { 3 List<List<Integer>> result = new ArrayList<>(); 4 List<Integer> current = new ArrayList<>(); 5 if(n <= 0 || n<k) 6 { 7 return result; 8 } 9 combine(n, k, 1, current, result); 10 return result; 11 } 12 //递归和n,k无关,只是边界条件和k相关,n,k在递归中不变化。 13 public void combine(int n, int k, int start, List<Integer> current, List<List<Integer>> result) 14 { 15 if(current.size() == k) 16 { 17 List<Integer> temp = new ArrayList<>(current); 18 result.add(temp); 19 return ; 20 } 21 for(int i = start; i <= n; i ++) 22 { 23 current.add(i); 24 combine(n, k, i+1, current, result); 25 current.remove(current.size()-1); 26 } 27 }
CombinationSum问题描述:给一组数字和target,这组数字没有重复元素,找出这组数字中相加为target的集合。
例如给定set[2,3,6,7]和target=7.返回[[7] [2,2,3]]. 也就是说,数字集合没有重复,但是结果集里面可以重复某个元素。先把数据集排序。
算法分析:利用递归,边界条件是target==0.
1 public List<List<Integer>> combinationSum(int[] candidates, int target) 2 { 3 List<List<Integer>> result = new ArrayList<>(); 4 List<Integer> current = new ArrayList<>(); 5 if(candidates == null || candidates.length == 0) 6 { 7 return result; 8 } 9 Arrays.sort(candidates); 10 combinationSum(candidates, target, 0, current, result); 11 return result; 12 } 13 14 public void combinationSum(int[] candidates, int target, int i, List<Integer> curr, List<List<Integer>> result) 15 { 16 if(target == 0)//组合成功 17 { 18 List<Integer> temp = new ArrayList<>(curr); 19 result.add(temp); 20 return; 21 } 22 for(int j = i; j < candidates.length; j ++) 23 { 24 if(target < candidates[j]) 25 { 26 return ; 27 } 28 curr.add(candidates[j]); 29 combinationSum(candidates, target-candidates[j], j, curr, result); 30 //如果上一步递归返回则去掉最后加入的元素,然后回溯。 31 //curr.remove(candidates[j]);错误,list的参数是index 32 curr.remove(curr.size()-1); 33 } 34 }
CombinationSum2问题描述:给定数据集里面有重复元素,但是结果集中每个数据集中的元素只能出现一次。
例如:[10, 1, 2, 7, 6, 1, 5],target=8,返回 17 125 26 116
算法分析:递归的下标要取j+1,然后还要过滤相同序列。使用hashset。
1 public List<List<Integer>> combinationSum2(int[] candidates, int target) { 2 List<List<Integer>> result = new ArrayList<>(); 3 List<Integer> current = new ArrayList<>(); 4 if(candidates == null || candidates.length == 0) 5 { 6 return result; 7 } 8 Arrays.sort(candidates); 9 combinationSum2(candidates, target, 0, current, result); 10 //利用hashset过滤重复元素 11 HashSet<List<Integer>> set = new HashSet<>(result); 12 result.clear(); 13 result.addAll(set); 14 return result; 15 } 16 17 public void combinationSum2(int[] candidates, int target, int i, List<Integer> curr, List<List<Integer>> result) 18 { 19 if(target == 0) 20 { 21 List<Integer> temp = new ArrayList<>(curr); 22 result.add(temp); 23 return; 24 } 25 for(int j = i; j < candidates.length; j ++) 26 { 27 if(candidates[j] > target) 28 { 29 return; 30 } 31 curr.add(candidates[j]); 32 //下标j+1,过滤相同元素 33 combinationSum2(candidates, target-candidates[j], j + 1, curr, result); 34 curr.remove(curr.size()-1); 35 } 36 }
CombinationSum3:给定k和target,找到1-9集合中满足k个数的和=target的序列集合。
例如:k=3,target=6.返回123 , k=3, target=9,返回126,135,234
算法分析:这道题和3Sum很像,因为3Sum不能用递归,因为时间复杂度太高。还有一类问题,就是说i<j<k, 返回i,j,k,,满足i+j+k=target
这种只能用三层for循环了,因为没有数据集的限制。
1 public List<List<Integer>> combinationSum3(int k, int n) { 2 List<List<Integer>> result = new ArrayList<>(); 3 List<Integer> curr = new ArrayList<>(); 4 combinationSum3(k, n, 1, curr, result); 5 return result; 6 } 7 8 public void combinationSum3(int k, int target, int i, List<Integer> curr, List<List<Integer>> result) 9 { 10 if(curr.size() == k && target == 0) 11 { 12 result.add(new ArrayList<Integer>(curr)); 13 return; 14 } 15 16 for(int j = i; j <= 9; j ++) 17 { 18 if(target < j) 19 { 20 return ; 21 } 22 curr.add(j); 23 combinationSum3(k, target - j, j+1, curr, result); 24 curr.remove(curr.size() - 1); 25 } 26 }