方法一:回溯算法
1.选择列表里的数,都是选择路径后面的数,使用一个参数start,来标识当前的选择列表的起始位置。标识每一层的状态,因此被形象的称为"状态变量"。
2.所有路径都应该加入结果集,所以不存在结束条件。或者说当start参数越过数组边界的时候,程序就自己跳过下一层递归了,因此不需要手写结束条件,直接加入结果集。
3.子集问题的选择列表,是上一条选择路径之后的数。
4.从递归树中看到,路径没有重复的,也没有不符合条件的,所以不需要剪枝。
class Solution {
public:
vector<vector<int>> res; //保存结果
void backtrack(vector<int>& track,vector<int>& nums,int start){
res.push_back(track); //直接满足结束条件
for(int i=start;i<nums.size();i++){
track.push_back(nums[i]); //做选择
backtrack(track,nums,i+1);
track.pop_back(); //撤销选择
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> track; //路径
backtrack(track,nums,0); //参数:路径,选择列表,选择列表起始位置
return res;
}
};
方法二:数学归纳
知道了[1,2]的子集,[ [], [1], [2], [1,2] ],那么[1,2,3]的子集就是[1,2]的子集都加上3,再加上[1,2]的子集。
subsets[1,2,3] = subsets[1,2] + subsets[1,2].push_back(3)
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int> > res(1); //包含一个空集
for(int i=0;i<nums.size();i++){
int cnt=res.size(); //前一个子集的个数
for(int j=0;j<cnt;j++){ //对前个子集的所有元素尾部加上添加的元素
vector<int> tmp=res[j];
tmp.push_back(nums[i]);
res.push_back(tmp);
}
}
return res;
}
};