水题:排列组合系列

39.组合总和

一开始我们很容易就写出一个合规,但是存在多余结果的一个版本
对于num上的数,做出一个dfs函数, 只要在每一层遍历数组,同时将可以减到left的结果压到res数组中。

但是这样,可能会出现重复的选择。即相邻递归层之间的数值互换的情况。

class Solution {
    vector<vector<int>> res_;
    vector<int> tmp_;
public:

    void helper(vector<int>& candidates, int left) {
        if(left == 0) {
            res_.push_back(tmp_);
            return;
        }else if(left < 0){
            return;
        }

        for(auto num : candidates) {
            tmp_.push_back(num);
            helper(candidates, left - num);
            tmp_.pop_back();
        }
    }

    vector<vector<int>> 
    combinationSum(vector<int>& candidates, int target) {
        helper(candidates, target);
        return std::move(res_);
    }
};

通过观察,我们发现,如果num < tmp.back(), 说明我们已经重复选择了。因此,只要把该条件加上即可。

class Solution {
    vector<vector<int>> res_;
    vector<int> tmp_;
public:

    void helper(vector<int>& candidates, int left) {
        if(left == 0) {
            res_.push_back(tmp_);
            return;
        }else if(left < 0){
            return;
        }

        for(auto num : candidates) {
            // 此处用去重
            if(tmp_.size() != 0 && tmp_.back() > num) {
                continue;
            }

            tmp_.push_back(num);
            helper(candidates, left - num);
            tmp_.pop_back();
        }
    }

    vector<vector<int>> 
    combinationSum(vector<int>& candidates, int target) {
        helper(candidates, target);
        return std::move(res_);
    }
};

但是,再观察可以得出一个结果,就是每次递归的所选的正确的下标,总是 > = >= >=上一个下标(即tmp.back()), 因此,我们可以通过在递归函数中,传递下标,来减少迭代次数。

class Solution {
    vector<vector<int>> res_;
    vector<int> tmp_;
public:

    void helper(vector<int>& candidates, int idx, int left) {
        if(left == 0) {
            res_.push_back(tmp_);
            return;
        }else if(left < 0){
            return;
        }

        for(int i = idx; i != candidates.size(); ++i) {
            int num = candidates[i];
            tmp_.push_back(num);
            helper(candidates, i, left - num);
            tmp_.pop_back();
        }
    }

    vector<vector<int>> 
    combinationSum(vector<int>& candidates, int target) {
        helper(candidates, 0, target);
        return std::move(res_);
    }
};

40. 组合总和 II

这个与39没多大区别,只是数组中的数不再是无限的,而只能用一次。

同时,由于数组的输入不是唯一的,可能还会存在重复的数。因此,我们需要做一个排序,使得相同的数都相邻(有没有三数之和的味道?), 然后在选数时加个判断,如果前一个数,跟当前数相同,那么一定已经选过了,我们需要剪枝。

class Solution {
    vector<vector<int>> res_;
    vector<int> tmp_;
public:

    void helper(vector<int>& candidates, int idx, int left) {
        if(left == 0) {
            res_.push_back(tmp_);
            return;
        }else if(left < 0){
            return;
        }

        for(int i = idx; i != candidates.size(); ++i) {
            if(i != idx && candidates[i] == candidates[i-1]) continue;
            int num = candidates[i];
            tmp_.push_back(num);
            
            // 改成 i + 1, 实现了数只能用一次.
            helper(candidates, i + 1, left - num);
            tmp_.pop_back();
        }
    }

    vector<vector<int>> 
    combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        helper(candidates, 0, target);
        return std::move(res_);
    }
};

46. 全排列

class Solution {
    vector<int>         tmp_;      
    vector<bool>        isVisited_; // Note: 状态数组
    vector<vector<int>> res_;
public:
    
    void helper(const vector<int>& nums){
        if(nums.size() == tmp_.size()){
            res_.push_back(tmp_);
            return;
        }

        for(int i = 0; i != isVisited_.size(); ++i) {
            if (isVisited_[i]  == true) continue;
            isVisited_[i] = true;
            tmp_.push_back(nums[i]);
            helper(nums);
            tmp_.pop_back();
            isVisited_[i] = false;
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        isVisited_.assign(nums.size(), false); 
        helper(nums);
        return res_;
    }
};

*47. 全排列 II

现在变成了排列问题,同时允许重复。单纯换了一下条件

首先我们需要排一下序使得重复的数字都相邻。然后在选取条件时,需要比较注意。如果前一个相同的数没被选,这意味着前一个数已经被选过了,因此我们需要跳过当前的这个数。

class Solution {
    vector<vector<int>> res;
    vector<int>         tmp;
    vector<bool>  isVisited;
public:
    void helper(vector<int>& nums, int curIdx){
        if(nums.size() == curIdx) {
            res.push_back(tmp);
            return;
        }

        for(int i = 0; i != nums.size(); i++) {
            if(isVisited[i] == true 
            || i > 0 && nums[i-1] == nums[i] && !isVisited[i-1]) continue;
            
            isVisited[i] = true;

            tmp.push_back(nums[i]);
            helper(nums, curIdx + 1);
            tmp.pop_back();

            isVisited[i] = false;
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        isVisited.assign(nums.size(), false);
        helper(nums, 0);
        return res;
    }
};

78. 子集

class Solution {
public:
    
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> bkt;

        
        bkt.push_back(vector<int>());
        for(int i = 0; i != nums.size(); ++i) {
            int len = bkt.size();
            for(int j = 0; j != len; ++j){
                vector<int> tmp;
                tmp.assign()

                tmp.push_back(nums[i]);

                bkt.push_back(tmp);
            }
        }

        return bkt;
    }
};

剑指 Offer II 079. 所有子集

78. 子集是一样的,这里提供另一个思路,从子集的概念出发。

思路: 根据子集的思路,对每个元素,要么选,要么不选。

通过二进制位来标识选择

class Solution {
public:

    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        int len = nums.size();

        // bits: 二进制位的不同状态个数
        // Note: 位运算的优先级问题
        for(int bits = 0; bits <= (1 << len) - 1; ++bits) {
            vector<int> tmp;

            for(int i = 0; i != len; i++){
                if( ((1 << i) & bits) != 0) {
                    tmp.push_back(nums[i]);
                }
            }

            res.push_back(tmp);
        }


        return res;
    }
};

用递归函数思路更简洁

class Solution {
    vector<vector<int>> res;
    vector<int>         tmp;

public:
    void helper(const vector<int>& nums, int curidx) {
        if(curidx == nums.size()) {
            res.push_back(tmp);
            return;
        }

        // 不选
        helper(nums, curidx + 1);

        // 选
        tmp.push_back(nums[curidx]);
        helper(nums, curidx + 1);
        tmp.pop_back();
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        helper(nums, 0);
        return res;
    }
};
  1. 子集 II
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值