题目描述(中等难度)
和上一道题非常像了,区别在于这里给的数组中有重复的数字,每个数字只能使用一次,然后同样是给出所有和等于 target 的情况。
解法一 回溯法
只需要在上题的基础上改一下就行了。直接看代码吧。
1. public List> combinationSum2(int[] candidates, int target) {
2. List
> ans = new ArrayList<>();
3. getAnswer(ans, new ArrayList<>(), candidates, target, 0);
4. /***修改的地方**/
5. // 如果是 Input: candidates = [2,5,2,1,2], target = 5,
6. // 输出会出现 [2 2 1] [2 1 2] 这样的情况,所以要去重
7. return removeDuplicate(ans);
8. /*****/
9. }
10. 11. private void getAnswer(List
> ans, ArrayList
**修改的地方**/
20. //i -> i + 1 ,因为每个数字只能用一次,所以下次遍历的时候不从自己开始
21. getAnswer(ans, temp, candidates, target - candidates[i], i + 1);
22. /********/
23. temp.remove(temp.size() - 1);
24. }
25. }
26. 27. }
28. 29. private List> removeDuplicate(List
> list) {
30. Map
> ans_list = new ArrayList
>();
42. for (String k : ans.keySet()) {
43. String[] l = k.split(",");
44. List
时间复杂度:
空间复杂度:
看到这里),想法很棒,为了解决重复的情况,我们可以先把数组先排序,这样就好说了。
1. public List> combinationSum2(int[] candidates, int target) {
2. List
> ans = new ArrayList<>();
3. Arrays.sort(candidates); //排序
4. getAnswer(ans, new ArrayList<>(), candidates, target, 0);
5. return ans;
6. }
7. 8. private void getAnswer(List
> ans, ArrayList
17. temp.add(candidates[i]);
18. /***修改的地方**/
19. //i -> i + 1 ,因为每个数字只能用一次,所以下次遍历的时候不从自己开始
20. getAnswer(ans, temp, candidates, target - candidates[i], i + 1);
21. /*******/
22. temp.remove(temp.size() - 1);
23. }
24. }
25. }
解法二 动态规划
怎么去更改上题的算法满足本题,暂时没想到,只想到就是再写个函数对答案再过滤一次。先记录给定的 nums 中的每个数字出现的次数,然后判断每个 list 的数字出现的次数是不是满足小于等于给定的 nums 中的每个数字出现的次数,不满足的话就剔除掉。如果大家有直接改之前算法的好办法可以告诉我,谢谢了。 此外,要注意一点就是上题中,给定的 nums 没有重复的,而这题中是有重复的。为了使得和之前一样,所以我们在算法中都得加上 1. if (i > 0 && nums[i] == nums[i - 1]) { 2. continue; 3. }
跳过重复的数字,不然是不能 AC 的,至于原因下边分析下。
1. public List> combinationSum2(int[] nums, int target) {
2. List
>> ans = new ArrayList<>(); //opt 数组
3. Arrays.sort(nums);// 将数组有序,这样可以提现结束循环
4. for (int sum = 0; sum <= target; sum++) { // 从 0 到 target 求出每一个 opt
5. List
> ans_sum = new ArrayList
>();
6. for (int i = 0; i < nums.length; i++) { //遍历 nums
7. /***修改的地方***/
8. if (i > 0 && nums[i] == nums[i - 1]) {
9. continue;
10. }
11. /********/
12. if (nums[i] == sum) {
13. List
> ans_sub = ans.get(sum - nums[i]);
18. //每一个加上 nums[i]
19. for (int j = 0; j < ans_sub.size(); j++) {
20. List
> removeDuplicate(List
> list) {
34. Map
> ans_list = new ArrayList
>();
48. for (String k : ans.keySet()) {
49. String[] l = k.split(",");
50. List
> remove(List
> list, int[] nums) {
62. HashMap
> ans = new ArrayList
>(list);
64. //记录每个数字出现的次数
65. for (int n : nums) {
66. int s = nh.getOrDefault(n, 0);
67. nh.put(n, s + 1);
68. }
69. for (int i = 0; i < list.size(); i++) {
70. List
如果不加跳过重复的数字的话,下边的样例不会通过。
这是因为我们求 opt 的时候每个列表的数量在以指数级增加,在上一个 opt 的基础上,每一个列表都增加 5 个 列表。
opt [ 1 ] = [ [ 1 ],[ 1 ],[ 1 ],[ 1 ],[ 1 ] ] 数量是 5,
opt [ 2 ] = [
[ 1,1 ], [ 1,1 ],[ 1,1 ],[ 1,1 ],[ 1,1 ],
[ 1,1 ], [ 1,1 ],[ 1,1 ],[ 1,1 ],[ 1,1 ]
[ 1,1 ], [ 1,1 ],[ 1,1 ],[ 1,1 ],[ 1,1 ],
[ 1,1 ], [ 1,1 ],[ 1,1 ],[ 1,1 ],[ 1,1 ],
[ 1,1 ], [ 1,1 ],[ 1,1 ],[ 1,1 ],[ 1,1 ],,
] 数量是 5 * 5。
opt [ 3 ] 数量是 5 * 5 * 5。
到了 opt [ 9 ] 就是 5 的 9 次方,数量是 1953125 内存爆炸了。
另一个算法也可以改一下
1. public List> combinationSum2(int[] nums, int target) {
2. List
>> ans = new ArrayList<>();
3. Arrays.sort(nums);
4. if (nums[0] > target) {
5. return new ArrayList
>();
6. }
7. for (int i = 0; i <= target; i++) {
8. List
> ans_i = new ArrayList
>();
9. ans.add(i, ans_i);
10. }
11. 12. for (int i = 0; i < nums.length; i++) {
13. /***修改的地方***/
14. if (i > 0 && nums[i] == nums[i - 1]) {
15. continue;
16. }
17. /********/
18. for (int sum = nums[i]; sum <= target; sum++) {
19. List
> ans_sum = ans.get(sum);
20. List
> ans_sub = ans.get(sum - nums[i]);
21. if (sum == nums[i]) {
22. ArrayList
> remove(List
> list, int[] nums) {
41. HashMap
> ans = new ArrayList
>(list);
43. for (int n : nums) {
44. int s = nh.getOrDefault(n, 0);
45. nh.put(n, s + 1);
46. }
47. for (int i = 0; i < list.size(); i++) {
48. List
如果不加跳过重复的数字的话,下边的样例不会通过
会发现出现了很多重复的结果,就是因为没有跳过重复的 1。在求 opt [ 1 ] 的时候就变成了 [ [ 1 ],[ 1 ] ] 这样子,由于后边求的时候都是直接在原来每一个列表里加数字,所有后边都是加了两次了。
总
和上题很像,基本上改一改就好了。排序的来排除重复的情况也很妙。还有就是改算法的时候,要考虑到题的要求的变化之处。
添加好友一起进步~
如果觉得有帮助的话,可以点击 这里 给一个 star 哦 ^^
如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情
