LeetCode算法题——Combination Sum I & II

来源:互联网 发布:php采集 编辑:程序博客网 时间:2024/06/14 15:15

这两题因为题设相近,且解题方法基本一致,因此集中讨论。

问题概述

I:

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]]

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.
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]]

分析

两道题目的题设中基本都是“给定数组并利用数组元素查找满足和为目标数的组合”。此类问题,直观的想法就是运用DFS的思想,用近似人的思考方式,将原始数组进行排序以后从小到大不断尝试组合,判断是否是否满足题意。而要做到这一点,最直接的方法就是运用递归。

观察发现,两道题目的区别仅仅为是否限制数组元素使用次数,因此实际上他们的大致解体思路是一样的。下面先给出问题I的代码,部分解释说明已附在代码中:

vector<vector<int>> combinationSum(vector<int>& candidates, int target) {    vector<vector<int>> result; // 存储满足题意组合的数组    vector<int> temp; // 临时数组,存储当前的组合    std::sort(candidates.begin(), candidates.end()); // 需要先对原始数组进行排序    getsum(candidates, target, result, temp, 0);    return result; }// 递归函数,处理当前的数组void getsum(vector<int>& candidates, int target, vector<vector<int> > &result, vector<int>& temp, int pos) {    if (!target) { // 当前尝试的组合满足题意,加入最终结果        result.push_back(temp);        return;    }    for (int i = pos; i < candidates.size() && target >= candidates[i]; i++) {        temp.push_back(candidates[i]); // 向组合中加入新的数        // 递归调用,可以发现新的目标数参数发生了相应改变;而由于每一个元素使用次数没有限制,因此        // pos参数仍然为i,即上一次遍历至的位置        getsum(candidates, (target-candidates[i]), result, temp, i);        temp.pop_back(); // 当前的组合数大于目标数,因此需要将组合最大的数弹出    }}

题目II的大致解体思路与之类似,只需要针对元素使用限制这一条件进行若干修改:

vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {    vector<vector<int>> result;    vector<int> temp;    std::sort(candidates.begin(), candidates.end());    getsum(candidates, target, result, temp, 0);    return result; } void getsum(vector<int>& candidates, int target, vector<vector<int> > &result,vector<int>& temp, int pos) {    if (!target) {        result.push_back(temp);        return;    }    for (int i = pos; i < candidates.size() && target >= candidates[i]; i++) {        temp.push_back(candidates[i]);        // 由于使用过的元素不能再使用,因此pos参数变为i+1,即需要从上一次遍历到的下一个元素开始递归        getsum(candidates, (target-candidates[i]), result, temp, i+1);        temp.pop_back();        // 需要跳过重复的组合        while (i+1 < candidates.size() && candidates[i] == candidates[i+1]) i++;    }}

总结

  • 解决此类组合数问题,往往需要运用DFS的思想,具体实现则可以借鉴递归
  • 灵活的调整递归函数的参数,要紧密结合题目的具体要求,例如是否限制元素的使用次数等等。