Combination Sum-leetcode No.39里引发的一些忽略点

leetcode上有这么一道题:

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]
]
大概意思就是给定一个int型数组candidates,和一个int型target,找到在数组里所有满足和为target的数组,注意:所有数均为正整数,candidates里的元素可以重复无限次,且组中结果里没有重复结果。
比如:candidates数组是:[2,3,6,7],target是7,那么所有满足要求的结果是:
[
  [7],
  [2,2,3]
]
这里面2就重复了。

【思路】
如果是刷题不多的同学,或者算法直觉不太好的同学,这种题一眼看起来似乎很难,可能性太多了,不能一一枚举吧。但这篇文章重点不讨论这个,只提一下思路就行,主要是讨论一下我的做法里面遇到的一些容易疏忽的小问题。

这种题思路如下:
这种结果再怎么复杂,基本也是枚举出来的,但是不能太暴力枚举。所以大概直觉是回溯,且打算采用递归。
以例子为例,大致原理如下:
首先将数组排序,从小到大,由于数组没有重复元素,在找解集时不用考虑重复元素的情况。
解集就在这些数形成的树中。树的每个下一级的元素,总是当前元素及其后面的数字(因为前面的数字没有意义,已经在之前处理过了,否则就重复了;依然从当前数开始,是因为允许重复),
且总要和sum相加,如果和依旧小于等于target,才会向下延伸,否则不加入树枝。

主要意思就是,根据和(sum)的情况,以及结合目前的和加上即将要相加的数字,如果这部分不超过target,则表示这一分支存在有解的可能性,就继续“深入”进去查找。否则:

>如果目前和小于target,且加上即将相加的数字和大于target,则舍弃目前最后一个数字,比如上图中最左边的解,当某个分支解为[2,2,2]时,3个2和为6了,如果再加上最小的2,已经超过了target=7,故没必要继续下去,且将这个2弹出(栈里为pop),剩下的解分支为:[2,2],转向2的下一个数字:3;

>如果目前和小于target,但是加上即将相加的数3,则可以进入下一步,正式加上3,和正好为target=7,则记录这个解,将其加入到解集中。此时这个支路已经找到解了,没必要再继续下去(因为继续下去和肯定大于target)。这里要注意一下:找到解时,要将记录解的栈中的最后一个数弹出,即3。

 

由于采用递归,每当加入一个数字,处理完其后续的解后,必须将此数字弹出,以便返回上一层,让上一层继续尝试这个数字之后的其他数字。

 

这篇笔记主要想提一个我忽略的问题,所以第一次写答案时的代码如下:

 1 public class Solution {
 2     List<List<Integer>> res=new ArrayList<List<Integer>>();
 3     LinkedList<Integer> al=new LinkedList<Integer>();
 4     int tSum;
 5     public List<List<Integer>> combinationSum(int[] candidates, int target) {
 6         int len=candidates.length;
 7         if(len==0)
 8             return res;
 9         Arrays.sort(candidates);
10         getCombinationSum(0,target,candidates);
11         return res;
12     }
13     public void getCombinationSum(int left,int target,int[]nums){
14         if(tSum>=target){
15             return;
16         }
17         int len=nums.length;
18         while(left<len&&nums[left]<=target&&tSum+nums[left]<=target){
19                 tSum+=nums[left];
20                 al.push(nums[left]);
21                 if(tSum==target){
22                     res.add(al);
23                     if(!al.isEmpty()){
24                         tSum-=al.peek();
25                         al.pop();
26                     }
27                     return;
28                 }
29                 getCombinationSum(left,target,nums);
30                 if(!al.isEmpty()){
31                     tSum-=al.peek();
32                     int t=al.pop();
33                 }
34                 left++;
35         }
36     }
37 }

 

但是最后答案个数是对的,但是每个解都是空的,比如:[[],[]]这种。

刚开始搞不明白哪里出错了,后来一想,将代码改成如下:

 1 public class Solution {
 2     List<List<Integer>> res=new ArrayList<List<Integer>>();
 3     LinkedList<Integer> al=new LinkedList<Integer>();
 4     int tSum;
 5     public List<List<Integer>> combinationSum(int[] candidates, int target) {
 6         int len=candidates.length;
 7         if(len==0)
 8             return res;
 9         Arrays.sort(candidates);
10         getCombinationSum(0,target,candidates);
11         return res;
12     }
13     public void getCombinationSum(int left,int target,int[]nums){
14         if(tSum>=target){
15             return;
16         }
17         int len=nums.length;
18         while(left<len&&nums[left]<=target&&tSum+nums[left]<=target){
19                 tSum+=nums[left];
20                 al.push(nums[left]);
21                 if(tSum==target){
22                     LinkedList<Integer> tal=new LinkedList<Integer>(al);
23                     res.add(tal);
24                     if(!al.isEmpty()){
25                         tSum-=al.peek();
26                         al.pop();
27                     }
28                     return;
29                 }
30                 getCombinationSum(left,target,nums);
31                 if(!al.isEmpty()){
32                     tSum-=al.peek();
33                     int t=al.pop();
34                 }
35                 left++;
36         }
37     }
38 }

 

可以发现,第22行左右这里不一样。因为这里al是全局的,且解集中每个解都是对al的引用,故最终处理完成时,al也弹完时,是空的,所以最后解集全是空的解,但是个数是对的。

基础不扎实,就会这么蠢-_-|||

 



posted @ 2017-02-28 09:38  慢羊羊slowsheepsheep  阅读(169)  评论(0)    收藏  举报