5.子集问题
子集问题的解题过程完全等价于组合问题,不同点在于组合问题搜集叶子节点上的结果,而子集需要收集每个节点的结果。其余方面,子集问题同样是不关心顺序的,{1, 2}与 {2, 1}同解,因此需要startIndex标记起始位置。
6.算法题
6.1子集
题解:
class Solution {
private:
vector<vector<int> > finalResult;
vector<int> curBuf;
void backtracking(vector<int>& nums, int startIndex)
{
finalResult.push_back(curBuf); // 搜集所有节点的结果,空集也需要,所以无需特判
if(startIndex == nums.size()) // 递归出口
return;
for(int i = startIndex; i < nums.size(); i++)
{
curBuf.push_back(nums[i]);
backtracking(nums, i+1); // 递归
curBuf.pop_back(); // 恢复现场
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
curBuf.clear(); // 初始化
finalResult.clear(); // 初始化
backtracking(nums, 0);
return finalResult;
}
};
注意:本题题干提示集合中没有重复元素,因此不需要对同一层上的元素进行限制。
6.2 子集2
题解:
class Solution {
private:
vector<vector<int> > finalResult;
vector<int> curBuf;
void backtracking(vector<int>& nums, int startIndex)
{
finalResult.push_back(curBuf); // 搜集所有节点的结果
if(startIndex == nums.size()) // 递归出口
return;
unordered_set<int> usedFlags; // 集合中存在负值,无法用数组表示usedFlags
for(int i = startIndex; i < nums.size(); i++)
{
if(usedFlags.find(nums[i]) == usedFlags.end()) // 未找到元素,返回指向尾部的迭代器,也即本层未使用过该元素
{
curBuf.push_back(nums[i]);
usedFlags.insert(nums[i]); // set中加入该元素表示本层出现过了
backtracking(nums, i+1);
curBuf.pop_back();
}
else // 本层使用过了,跳过本次
continue;
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
curBuf.clear();
finalResult.clear();
sort(nums.begin(), nums.end()); // 避免重复元素导致的如{1,2,3} {1,3,2}
backtracking(nums, 0);
return finalResult;
}
};
注意:该题集合中存在重复元素,与组合问题中避免重复解的操作一样,先排序再用局部变量标记本层元素的使用情况,由于集合中存在负值所以无法使用 int usedFlags[] 去标记(负值索引不合法,如usedFlags[-1].)因此采用unorderd_set标记。