代码随想录算法训练营第29天 | 回溯算法part05:● 491.递增子序列 ● 46.全排列 ● 47.全排列 II

#491 递增子序列  有点难  30min 找不到合适的去重逻辑了,于是没办法用了set<vector<int>> 通过了

“90子集ii 中我们是通过排序,再加一个标记数组来达到去重的目的。 而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。 所以不能使用之前的去重逻辑!”

用[4, 7, 6, 7] 或者 [4,6,7,5,7] 都能体现这个题的特点,正确的逻辑是:同一父节点下的同层上使用过的元素就不能再使用了

然后本来想用 unordered_set<vector<int>>发现不行,还要自定hash func,但换成set就行了 

 我的code:用set的,comment掉的是走不通的去重逻辑

    set<vector<int>> res;
    vector<int> vec;

    void backtrack(int startIdx, vector<int>& nums){
        if(startIdx>=nums.size()) return;
        
        for(int i=startIdx; i<nums.size();i++){
            if(vec.size()!=0 && nums[i]<vec[vec.size()-1]) continue;
            //if(vec.size()!=0 && i!=startIdx && nums[i-1]==nums[i]) continue;
            vec.push_back(nums[i]);
            if(vec.size()>=2) res.insert(vec);
            backtrack(i+1,nums);
            vec.pop_back();
        }
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        vec.clear();
        backtrack(0,nums);
        vector<vector<int>> result(res.begin(), res.end());
        return result;
    }

根据随想录修改过后的:其实就是加了个unordered_set<int> uset;(每层都有一个自己的新的),同一父节点下的同层上使用过的元素就不能再使用了。 

学到了vec最后一个元素可以直接用.back()

    set<vector<int>> res;
    vector<int> vec;

    void backtrack(int startIdx, vector<int>& nums){
        if(startIdx>=nums.size()) return;

        unordered_set<int> uset; // 使用set来对本层元素进行去重
        for(int i=startIdx; i<nums.size();i++){
            if ((!vec.empty() && nums[i] < vec.back())
            || uset.find(nums[i]) != uset.end()) {
                continue;
            }
            uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
           
            vec.push_back(nums[i]);
            if(vec.size()>=2) res.insert(vec);
            backtrack(i+1,nums);
            vec.pop_back();
        }
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        vec.clear();
        backtrack(0,nums);
        vector<vector<int>> result(res.begin(), res.end());
        return result;
    }

#46 全排列 25min, 主要就是去重

弄了一个set去重,但是一开始没写对,在push 到 res那里就直接clear了,结果WA,一个个print出来发现不对,set也应该加入回溯

一个个print debug真的有点慢的,尤其是写print还不熟练的时候,比如print set:用for auto ele:set 就行

    vector<vector<int>> res;
    vector<int> vec;
    set<int> used;

    void backtrack(vector<int>& nums){
        if(vec.size()==nums.size()){
            res.push_back(vec);
            return;
        }

        for(int i=0;i<nums.size();i++){
            if(used.find(nums[i])!=used.end()) continue;
            vec.push_back(nums[i]);
            used.insert(nums[i]);
            backtrack(nums);
            vec.pop_back();
            used.erase(nums[i]);
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        backtrack(nums);
        return res;
    }

啊其实改了一下发现很容易,没什么怕错的:然后我的set当做 global var因为我有点怕放在传参数那里会传错,但是还是要练一下再写一版。然后随想录和我的区别就是不用的set,用的是

vector<bool> used(nums.size(), false);
    vector<vector<int>> res;
    vector<int> vec;
    

    void backtrack(vector<int>& nums, set<int> used){
        if(vec.size()==nums.size()){
            res.push_back(vec);
            return;
        }

        for(int i=0;i<nums.size();i++){
            if(used.find(nums[i])!=used.end()) continue;
            vec.push_back(nums[i]);
            used.insert(nums[i]);
            backtrack(nums,used);
            vec.pop_back();
            used.erase(nums[i]);
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        set<int> used;
        backtrack(nums,used);
        return res;
    }

 下面也放一下随想录的46代码,这样47题看总结时可以参考

    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }

时间复杂度不用看代码,直接想做排列就是n! 

#47

先是把#46的set里放value改成set里放index,这里注意一点是,#46有两种实现方法,我的是放到set里,随想录是弄一个bool表。我这种放到set里面,必须要把原来放value改成放index,不然不行。但他那种弄bool表的,只需要把原来这一句不要再写就行

 if (used[i] == true) continue; // path里已经收录的元素,直接跳过

然后准备去重,一时没想到去重逻辑,偷个懒,先用set of vec做一下,过了:

    set<vector<int>> res;
    vector<int> vec;
    
    void backtrack(vector<int>& nums, set<int> used){
        if(vec.size()==nums.size()){
            res.insert(vec);
            return;
        }

        for(int i=0;i<nums.size();i++){
            if(used.find(i)!=used.end()) continue;
            vec.push_back(nums[i]);
            used.insert(i);
            backtrack(nums,used);
            vec.pop_back();
            used.erase(i);
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        set<int> used;
        backtrack(nums,used);
        vector<vector<int>> result(res.begin(),res.end());
        return result;
    }

好,现在来讲正经去重逻辑,本题是 40.组合总和II 去重逻辑 和 46.全排列 的结合

重中之重是这句:if (i > 0 && nums[i] == nums[i - 1] && used.find(i-1)!=used.end()) continue;

排序后,前后相等的元素,而且前一个用过了,就不会让再来一次了

举个例子:

idx [0, 1, 2 ]   [1, 0, 2 ]

val [1, 1, 2 ]   [1, 1, 2 ]

就这两个东西不应该都出现的,这句就能保证

    vector<vector<int>> res;
    vector<int> vec;
    

    void backtrack(vector<int>& nums, set<int> used){
        if(vec.size()==nums.size()){
            res.push_back(vec);
            return;
        }

        for(int i=0;i<nums.size();i++){
            if(used.find(i)!=used.end()) continue;
            if (i > 0 && nums[i] == nums[i - 1] && used.find(i-1)!=used.end()) continue;
            
            vec.push_back(nums[i]);
            used.insert(i);
            backtrack(nums,used);
            vec.pop_back();
            used.erase(i);
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        set<int> used;
        backtrack(nums,used);
        return res;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值