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]
]
这道题是很经典的运用回溯法解决的例子。运用回溯法解题,最关键是要找到三个关键点:初始状态、临界状态和迭代过程。我们通过这道题分别对这三个点进行简要地分析。
这道题给出了一个数组,要求我们利用数组中的数字找出所有和为target的情况,允许数组元素重复使用。为了方便找出所有情况,我们先对数组进行排序。
首先我们来考虑初始状态,很显然,在刚开始的时候,vector为空,要寻找的和为target。
再来看看临界情况,同样也很明显,当我们匹配到所要寻找的target为0时,说明此时这种匹配情况成立,将这种情况存进vector中即可。当target小于0时,说明匹配不成立,结束继续往深处遍历的过程。
最后再来看看迭代的过程,当我们选定一个数candidates[i]之后,那么此时匹配的总和为(target-i),由于可以重复,因此仍将从candidates[i]开始匹配。这样一个个数下去,找得到符合要求的数字就存进vector中,否则就返回,将该数pop掉,将下一个数存进数组中重新考虑情况,直到原数组遍历完成为止。
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> v;
vector<int> vec;
sort(candidates.begin(), candidates.end());
combinate(v, vec, candidates, target, 0);
return v;
}
void combinate(vector<vector<int>>& v, vector<int>& vec, vector<int>& candidates, int target, int start) {
if (target < 0) {
return;
}
if (target == 0) {
v.push_back(vec);
return;
}
for (int i = start; i < candidates.size(); i++) {
vec.push_back(candidates[i]);
combinate(v, vec, candidates, target - candidates[i], i);
vec.pop_back();
}
}
};
Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 .
Example:
Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]Note:
The length of the given array will not exceed 15.
The range of integer in the given array is [-100,100].
The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.
看到这道题,可能第一感觉是这道题跟回溯法的结合不是很紧密。但其实,我们可以通过上面题目中的例子先来思考一下回溯法的流程。
题目中数组为[4,6,7,7],根据题目的要求(数组递增),我们可以先来这样思考。首先很明显的是[4,6]符合题目的要求,一旦符合要求,就继续往后遍历直到数组遍历完成,因此[4,6,7]和[4,6,7,7]也符合情况。
在数组遍历完成之后,我们就尝试将数组后面的元素pop掉,跳过之前选中的元素,于是有[4,7]符合要求。和上面一样重复遍历的过程,[4,7,7]也满足要求。
依此类推,可以知道[6,7]、[6,7,7]和[7,7]也满足,这样结果就出来了。
通过上面的例子我们可以发现,其实上面的流程就是回溯法的流程,因此和之前一样,找准三个关键点。
首先初始情况下,数组vector为空,从第一个元素开始遍历,这里就没什么好说的了。
然后临界状态,就是当vector元素的数目大于1时,说明符合要求,将其输出。
最后再来看看迭代过程。由于数组要递增,所以在每遍历一个元素的时候,需要将其与vector中最后一个元素进行比较,如果满足大于等于的关系,就将其push到vector中。然后在选中当前元素之后的情况全部考虑完之后,就将其pop掉,考虑后面的元素,继续前面的过程,直到全部情况考虑完毕,这样问题就顺利解决了。
在这里需要注意的是,由于要求不能重复,所以在插入元素的时候,还需要进行判断,这里用了unordered_set,其原理是使用红黑二叉树,时间复杂度为O(logn),可以最大程度提高程序执行的效率。
class Solution {
public:
vector<vector<int>> findSubsequences(vector<int>& nums) {
vector<vector<int>> v;
vector<int> vec;
subsequences(v, vec, nums, 0);
return v;
}
void subsequences(vector<vector<int>>& v, vector<int>& vec, vector<int>& nums, int start) {
if (vec.size() >= 2) {
v.push_back(vec);
}
unordered_set<int> set;
for (int i = start; i < nums.size(); i++) {
if ((vec.size() == 0 || nums[i] >= vec.back()) && (set.find(nums[i]) == set.end())) {
vec.push_back(nums[i]);
subsequences(v, vec, nums, i + 1);
vec.pop_back();
set.insert(nums[i]);
}
}
}
};
以上是我对这两道回溯问题的一些想法,有问题还请在评论区讨论留言~