【Leetcode】Combination Sum II

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.

For example, given candidate set 10,1,2,7,6,1,5 and target 8
A solution set is: 
[1, 7] 
[1, 2, 5] 
[2, 6] 
[1, 1, 6] 


思路和解法

整个解法分为iterative部分和recursive部分,两个部分加起来,构成完整的解法:
  • 对当前节点所有可能进行遍历使用的是iterative方法。
  • 对每一个当前节点的可能,去寻找之后节点的可能用recursive方法。

Combination Sum II 这一题,主体框架是上文说的iterative+recursive,但是由于对每个当前节点,可能的状态只有两种,即选择和不选择,因此代码中实际没有对节点状态用for循环,而是直接把两种状态写出来。

这一题除了主体部分,还要考虑的一个问题是避免重复。为了避免重复有两种方法:

方法1:
  • 对当前节点,先选择自己,然后recurse到下一个节点
  • 回来后,去除自己(即不选自己),然后去除接下来和自己重复的节点,到第一个不重复的节点开始做recursion

核心是:如果不选某一个节点,则不选接下来的和该节点相同的节点
code如下:

https://leetcode.com/discuss/11278/my-solution-without-using-set


void comb(vector<int>& num, int st, int target, vector<int>& psol, vector<vector<int> >& result){
      //processing num[st...] ,  psol: partial solution
        if(target==0){
            result.push_back(psol);
        }else{
            if( st==num.size() ) return; //cannot make it return directly
            int newtarget = target - num[st];
            if(newtarget>=0){
                psol.push_back(num[st]);
                comb(num, st+1, newtarget, psol, result);
                psol.pop_back();
                int i=st+1;
                while( i<num.size() && num[i]==num[i-1] ) { i++; } //skip dups
                comb(num, i, target, psol, result); //skip all values == num[st]

            }//else cannot make it stop immediately
        }

    }
    vector<vector<int> > combinationSum2(vector<int> &num, int target) {//O(2^n) time + O(n) space
        vector<vector<int> > result;
        vector<int> psol;
        std::sort(num.begin(), num.end());
        comb(num, 0, target, psol, result);
        return result;
    }


方法2:
  • 对当前节点,先不选自己,然后recurse到下一个节点。
  • 回来后,选自己,并选择接下来和自己一样的节点,然后在和自己不一样的节点处,做recursion。

核心:如果选择某一个节点,则一定选择接下来跟它相同的所有节点。
code如下(我的解法):
class Solution {
public:
    vector<vector<int> > combinationSum2(vector<int> &num, int target) {
        vector<int> pre;
        vector<vector<int>> res;
        sort(num.begin(), num.end());
        trySum(num, pre, target, 0, res);
        return res;
    }
    
    void trySum(vector<int> &num, vector<int> &pre, int target, int index, vector<vector<int>> &res) {
        int len = num.size();
        if(target==0) {
            res.push_back(pre);
            return;
        } else if(index>=len || target<num[index]) { // 这一句是一个重要的剪枝,如果当前节点比target都大了,由于已经排序了,那么后面的节点也一定比target大,那么也没有必要继续往后找了
            return;
        }
        
        if(index>=len) return;
        trySum(num, pre, target, index+1, res);
        int new_index = 0;
        for(new_index=index; new_index<len; new_index++) { // if we choose A, we must also choose the A's following it. This is designed to avoid duplicate combinations
            if(num[new_index] == num[index]) {
                target -= num[index];
            } else {
                break;
            }
        }
        int ori_size = pre.size();
        pre.insert(pre.end(), new_index-index, num[index]);
        trySum(num, pre, target, new_index,res);
        pre.erase(pre.begin()+ori_size, pre.end());
        return;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值