剑指offer专项突击版第27天

剑指 Offer II 079. 所有子集

递归
每帧递归栈只需要选当前元素与不选当前元素即可,记得回溯时要恢复现场了。

class Solution {
private:
    vector<int> tmp;    
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        dfs(0,nums,res);
        return res;
    }

    void dfs(int x, vector<int> &nums, vector<vector<int>> &res) {
        if(x == nums.size()) {
            res.emplace_back(tmp);
            return;
        }
        dfs(x+1,nums,res);
        tmp.emplace_back(nums[x]);
        dfs(x+1,nums,res);
        tmp.pop_back();
    }
};  

迭代实现

class Solution { 
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int len = nums.size();
        int n = 1<<len;
        vector<vector<int>> res;
        vector<int> tmp;
        for(int i = 0; i < n; i++) {
            tmp.clear();
            for(int j = 0; j < len; j++) {
                if(i&(1<<j)) {
                    tmp.emplace_back(nums[j]);
                }
            }
            res.emplace_back(tmp);
        }
        return res;
    }
};  

剑指 Offer II 080. 含有 k 个元素的组合

最容易想到的方法获得所有组合方式,从中筛选出我们所需的,时间复杂度 O ( 2 n ) O(2^n) O(2n)

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> res;
        vector<int> tmp;
        dfs(1,n,k,tmp,res);
        return res;
    }
    void dfs(int x, int n, int k, vector<int> &tmp, vector<vector<int>> &res) {
        if(tmp.size() == k) {
            res.emplace_back(tmp);
            return;
        } else if(x == n+1) return;

        dfs(x+1,n,k,tmp,res);
        tmp.emplace_back(x);
        dfs(x+1,n,k,tmp,res);
        tmp.pop_back();
    }
};

递归剪枝优化
如果剩下的数不够选就直接退出递归,时间复杂度就等于 O ( C k n ) O(C^n_{k}) O(Ckn)

class Solution {
private:

public:
    vector<vector<int>> res;
    vector<int> tmp;
    int n,k;
    vector<vector<int>> combine(int n, int k) {
        this->n = n;
        this->k = k;
        dfs(1,1);
        return res;
    }
    void dfs(int select, int start) {
        if(n-start+1 < k-select+1) return; //剪枝,如果剩下的数不够选就直接退出
        if(select == k+1) {
            res.emplace_back(tmp);
            return;
        }
        for(int i = start; i <= n; i++) {
            tmp.emplace_back(i);
            dfs(select+1, i+1);
            tmp.pop_back();
        }
    }
};

消除递归,大神做法(巨难理解!)
解释

class Solution {
public:
    vector<int> temp;
    vector<vector<int>> ans;

    vector<vector<int>> combine(int n, int k) {
        // 初始化
        // 将 temp 中 [0, k - 1] 每个位置 i 设置为 i + 1,即 [0, k - 1] 存 [1, k]
        // 末尾加一位 n + 1 作为哨兵
        for (int i = 1; i <= k; ++i) {
            temp.push_back(i);
        }
        temp.push_back(n + 1);
        
        int j = 0;
        while (j < k) {
            ans.emplace_back(temp.begin(), temp.begin() + k);
            j = 0;
            // 寻找第一个 temp[j] + 1 != temp[j + 1] 的位置 t
            // 我们需要把 [0, t - 1] 区间内的每个位置重置成 [1, t]
            while (j < k && temp[j] + 1 == temp[j + 1]) {
                temp[j] = j + 1;
                ++j;
            }
            // j 是第一个 temp[j] + 1 != temp[j + 1] 的位置
            ++temp[j];
        }
        return ans;
    }
};

剑指 Offer II 081. 允许重复选择元素的组合

二叉树结构

class Solution {
public:
    void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
        if (idx == candidates.size()) {
            return;
        }
        if (target == 0) {
            ans.emplace_back(combine);
            return;
        }
        // 直接跳过
        dfs(candidates, target, ans, combine, idx + 1);
        // 选择当前数
        if (target - candidates[idx] >= 0) {
            combine.emplace_back(candidates[idx]);
            dfs(candidates, target - candidates[idx], ans, combine, idx);
            combine.pop_back();
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> ans;
        vector<int> combine;
        dfs(candidates, target, ans, combine, 0);
        return ans;
    }
};

多叉树结构(更省空间,似乎是少了很多函数栈帧)

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> path;
        dfs(candidates, path, res, target, 0);
        return res;
    }

    void dfs(vector<int> &candidates, vector<int> &path, vector<vector<int>> &res, int target, int idx){
        if (target == 0){
            res.push_back(path);
            return;
        }
        for (int i = idx; i < candidates.size(); i++){
            if(target-candidates[i] >= 0) {
                path.push_back(candidates[i]);
                dfs(candidates, path, res, target-candidates[i], i);
                path.pop_back();
            }
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值