491. 递增子序列(题目链接:力扣)
思路:子集问题,需要收集整颗树的节点(根据题目要求元素的个数至少要两个,所以要收集深度大于等于2的节点),这题有点小坑,就是不能将数组排序,因此无法用startIndex+1的方式来进行层内的去重(但是仍需要startIndex+1的方式来保证节点是数组从前往后遍历的)。所以每层递归需要额外声明一个unordered_set集合,用来记录本层用过了的数值,保证层内所有节点数值不重复。
vector<vector<int>> result;
void backtracking(const vector<int>& nums, vector<int>& tmpSum, int startIndex){
if(tmpSum.size() > 1){
result.push_back(tmpSum);
}
unordered_set<int> uset;
for(int i=startIndex; i<nums.size(); i++){
if(!tmpSum.empty() && nums[i]<tmpSum.back() || uset.find(nums[i])!=uset.end()) continue;
uset.insert(nums[i]);
tmpSum.push_back(nums[i]);
backtracking(nums, tmpSum, i+1);
tmpSum.pop_back();
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
vector<int> tmpSum;
backtracking(nums, tmpSum, 0);
return result;
}
46. 全排列(题目链接:力扣)
思路:全排列问题,收集的是树的所有叶子节点。因为数组不含重复元素,因此每次添加只需判断该元素是否已经使用过,因此只需定义一个数组vector<bool>uset(nums.size(),false)来记录每个元素的使用情况即可。
vector<vector<int>> result;
void backtracking(vector<int>& nums, vector<int>& tmpSum, vector<bool>& uset){
if(tmpSum.size() == nums.size()){
result.push_back(tmpSum);
return;
}
for(int i=0; i<nums.size(); i++){
if(uset[i]) continue;
uset[i] = true;
tmpSum.push_back(nums[i]);
backtracking(nums, tmpSum, uset);
tmpSum.pop_back();
uset[i] = false;
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<int> tmpSum;
vector<bool> uset(nums.size(), false);
backtracking(nums, tmpSum, uset);
return result;
}
47. 全排列 II(题目链接:力扣)
思路:同上题,题解依然是树的所有叶子节点。但是数组内有重复元素,层内需要保证每个节点的值是不一样的。同一路径需要保证所用到的元素是唯一的(数值可以相等,因为数组有相同值元素,但是下标唯一),因此仍然需要vector<bool>uset(nums.size(),false)用来记录每个元素的使用情况,保证每次添加唯一。
vector<vector<int>> result;
void backtracking(vector<int>& nums, vector<int>& tmpSum, vector<bool>& uset){
if(tmpSum.size() == nums.size()){
result.push_back(tmpSum);
return;
}
unordered_set<int> isUse;
for(int i=0; i<nums.size(); i++){
if(uset[i] || !isUse.empty() && isUse.find(nums[i])!=isUse.end()) continue;
isUse.insert(nums[i]);
uset[i] = true;
tmpSum.push_back(nums[i]);
backtracking(nums, tmpSum, uset);
tmpSum.pop_back();
uset[i] = false;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<int> tmpSum;
vector<bool> uset(nums.size(), false);
backtracking(nums, tmpSum, uset);
return result;
}
其实还有一种不用unordered_set<int> isUse的方法,但是理解起来有点绕。首先对数组进行排序,层内判断时,如果上一个值相同的元素已经用过了,那么本层就不能用这个值了。我的理解是多个相同值的首个元素是可以用到的(这很好理解),所以后面在看到相同的就不能再用了(同一层内)。但是同一路径上是可以保持有相同值的,所以需要额外加uset[i-1]==false判断(为true的话说明祖先节点有加进来,祖先都用了那么我肯定可以用,换言之,我不能比祖先先用),否则会全部跳过。
vector<vector<int>> result;
void backtracking(vector<int>& nums, vector<int>& tmpSum, vector<bool>& uset){
if(tmpSum.size() == nums.size()){
result.push_back(tmpSum);
return;
}
for(int i=0; i<nums.size(); i++){
if(i>0 && nums[i]==nums[i-1] && uset[i-1]==false) continue;
if(uset[i]==false){
uset[i] = true;
tmpSum.push_back(nums[i]);
backtracking(nums, tmpSum, uset);
tmpSum.pop_back();
uset[i] = false;
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<int> tmpSum;
vector<bool> uset(nums.size(), false);
backtracking(nums, tmpSum, uset);
return result;
}