代码随想录算法训练营day29 | LeetCode 491. 递增子序列 46. 全排列 47. 全排列 II

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_porter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值