Leetcode组合总和1-4题题解 Leecode最近几天的每日一题都是组合总和问题,预测明天是组合总和Ⅳ,因此,提前将组合总和的所有题目刷了,前三题的思路都差不多,最后一题做法有所不同:
组合总和:candidates
中的数字可以无限制重复被选取。
组合总和Ⅱ: candidates
中的每个数字在每个组合中只能使用一次。
组合总和Ⅲ:组合中只允许有1-9的数字,并且每种组合中不存在重复的数字。
组合总和Ⅳ:找出符合要求组合的个数。
给定一个无重复元素 的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
所有数字(包括 target
)都是正整数。
解集不能包含重复的组合。
示例 1:
1 2 3 4 5 6 输入:candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]
示例 2:
1 2 3 4 5 6 7 输入:candidates = [2,3,5], target = 8, 所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate
中的每个元素都是独一无二的。
1 <= target <= 500
题解(dfs,回溯算法) 分析: 此类问题可以画出树形图,然后就会发现此题可以用dfs+回溯算法解决,用到的数据结构为双端队列,具有栈和队列的性质,其定义方式为:Deque stack = new ArrayDeque();具体步骤见代码。
具体步骤如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution { public List<List<Integer>> combinationSum(int [] candidates, int target) { List<List<Integer>> res = new ArrayList(); int len = candidates.length; if (len == 0 ){ return res; } Deque<Integer> path = new ArrayDeque(); dfs(candidates,0 ,len,target,path,res); return res; } public void dfs (int [] candidates,int start,int len,int target,Deque<Integer> path,List<List<Integer>> res) { if (target < 0 ){ return ; } if (target == 0 ){ res.add(new ArrayList(path)); } for (int i = start; i < len; i++){ path.addLast(candidates[i]); dfs(candidates,i,len,target - candidates[i],path,res); path.removeLast(); } } }
给定一个数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
1 2 3 4 5 6 7 8 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2:
1 2 3 4 5 6 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
题解(dfs,回溯算法,哈希表) 分析: 此题和组合总和 的区别在于 candidates
中的每个数字在每个组合中只能使用一次,并且解集不能包含重复的元素,因此可用哈希表对重复解集去重,具体步骤看下方代码注释。
具体步骤如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Solution { public List<List<Integer>> combinationSum2(int [] candidates, int target) { Arrays.sort(candidates); int len = candidates.length; List<List<Integer>> res = new ArrayList(); Deque<Integer> path = new ArrayDeque(); dfs(candidates,0 ,len,target,path,res); HashSet<List<Integer>> set = new HashSet(); for (List<Integer> list : res){ set.add(list); } return new ArrayList(set); } public void dfs (int [] candidates,int start,int len,int target,Deque<Integer> path,List<List<Integer>> res) { if (target < 0 ){ return ; } if (target == 0 ){ res.add(new ArrayList(path)); } for (int i = start; i < len; i++){ path.addLast(candidates[i]); dfs(candidates,i+1 ,len,target - candidates[i],path,res); path.removeLast(); } } }
找出所有相加之和为 n 的 k 个数的组合。 组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
示例 1:
1 2 输入: k = 3, n = 7 输出: [[1,2,4]]
示例 2:
1 2 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]]
题解(dfs,回溯算法) 分析: 此题和组合总和Ⅱ 的区别在于在1-9中选择数据,并且每个数据只能选一次,且只需返回长度为k的路径,因此需对结果集进行筛选,具体步骤看下方代码注释。
具体步骤如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution { public List<List<Integer>> combinationSum3(int k, int n) { int [] arr = new int []{1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 }; List<List<Integer>> res = new ArrayList(); List<List<Integer>> res1 = new ArrayList(); Deque<Integer> path = new ArrayDeque(); dfs(arr,0 ,n,path,res); for (List<Integer> list : res){ if (list.size() == k){ res1.add(list); } } return res1; } public void dfs (int [] arr,int start,int n ,Deque<Integer> path,List<List<Integer>> res) { if (n < 0 ){ return ; } if (n == 0 ){ res.add(new ArrayList(path)); } for (int i = start; i < 9 ; i++){ path.addLast(arr[i]); dfs(arr,i + 1 ,n - arr[i],path,res); path.removeLast(); } } }
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 nums = [1, 2, 3] target = 4 所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1) 请注意,顺序不同的序列被视作不同的组合。 因此输出为 7。
进阶: 如果给定的数组中含有负数会怎么样? 问题会产生什么变化? 我们需要在题目中添加什么限制来允许负数的出现?
题解(1.dfs,回溯算法 2.动态规划) 分析: 此题和组合总和 类似,区别在于求出所有解集后,还需求出解集的全排列,并返回全排列的个数。
组合总数前三题都是同样的套路,只是在结果处理以及中间过程有略微差别,但是这题不同的是要求结果集的全排列,因此,我就想用第一题的算法 + 全排列算法求出此题,代码如demo1
,结果超时,代码逻辑是没有问题的,但题目所给数据过大,导致算全排列的时候使用过多时间,因此未通过。
查看题解,发现正确的解法为动态规划,根据分析可以得到状态转移方程:
dp[i] = dp[i - nums[0]] + dp[i - nums[1]] + dp[i - nums[2]]......
例如nums = [1,3,4],target = 7;
dp[7] = dp[6] + dp[4] + dp[3];
具体代码见demo2
具体步骤如下: demo1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 class Solution { public int combinationSum4 (int [] nums, int target) { Arrays.sort(nums); int sum = 0 ; List<List<Integer>> res = new ArrayList(); int len = nums.length; Deque<Integer> path = new ArrayDeque(); dfs(nums,0 ,len,target,path,res); for (List<Integer> list : res){ sum += isok(list); } return sum; } public void dfs (int [] nums,int start,int len,int target,Deque<Integer> path,List<List<Integer>> res) { if (target < 0 ){ return ; } if (target == 0 ){ res.add(new ArrayList(path)); } for (int i = start; i < len; i++){ path.addLast(nums[i]); dfs(nums,i,len,target - nums[i],path,res); path.removeLast(); } } public int isok (List<Integer> list) { int [] nums = new int [list.size()]; for (int i = 0 ; i < nums.length; i++){ nums[i] = list.get(i); } int len = nums.length; Deque<Integer> path = new ArrayDeque(); List<List<Integer>> res = new ArrayList(); boolean [] used = new boolean [len]; dfs2(nums,len,0 ,used,path,res); return res.size(); } public void dfs2 (int [] nums,int len,int depth,boolean [] used,Deque<Integer> path,List<List<Integer>> res) { if (depth == len){ res.add(new ArrayList(path)); return ; } for (int i = 0 ; i < len; i++){ if (used[i]){ continue ; } if (i > 0 && nums[i] == nums[i - 1 ] && !used[i - 1 ]){ continue ; } path.addLast(nums[i]); used[i] = true ; dfs2(nums,len,depth + 1 ,used,path,res); path.removeLast(); used[i] = false ; } } }
demo2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int combinationSum4 (int [] nums, int target) { int [] dp = new int [target + 1 ]; dp[0 ] = 1 ; for (int i = 0 ; i <= target; i++){ for (int num : nums){ if (num <= i){ dp[i] += dp[i - num]; } } } return dp[target]; } }
:smile:以上题解仅限于个人理解,如有更好的方法或者更高效的解法,请移步至评论区,谢谢!