Backtracing: Subsets I+II, Permutations I+II, Combination Sum I+II

Subsets

vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
    vector<int> cur;
    backtrack(nums,cur,0);
    return res;
}
void backtrack(vector<int> &nums, vector<int> &cur, int start){
    res.push_back(cur);
    for (int i=start;i<nums.size();++i){
        cur.push_back(nums[i]);
        backtrack(nums,cur,i+1);
        cur.pop_back();
    }
}

 

Subsets II

先排序,方便跳过重复元素。同一层递归中相同元素跳过。

vector<vector<int>> res;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
    sort(nums.begin(),nums.end());
    vector<int> cur;
    backtrack(nums,cur,0);
    return res;
}
void backtrack(vector<int> &nums, vector<int> &cur, int start){
    res.push_back(cur);
    for (int i=start;i<nums.size();++i){
        if (i>start && nums[i]==nums[i-1]) continue;
        cur.push_back(nums[i]);
        backtrack(nums,cur,i+1);
        cur.pop_back();
    }
}

 

Subsets 时间复杂度

T(n) = T(n-1)+T(n-2)+...+T(1)+T(0) -> T(n) = O(2^n)

如果考虑 res.push_back(cur) 是时间 O(n),那么总的时间复杂度为 O(n*2^n)

Subsets 空间复杂度 

O(n) 递归深度最多n。

 

====================================================================

Permutations

vector<vector<int>> res;
vector<vector<int>> permute(vector<int> &nums) {
    vector<int> cur;
    vector<bool> used(nums.size(),false);
    backtrack(nums,cur,used);
    return res;
}
void backtrack(vector<int> &nums, vector<int> &cur, vector<bool> &used){
    if (cur.size()==nums.size()){ res.push_back(cur); return; }
    for (int i=0;i<nums.size();++i){
        if (used[i]) continue;
        cur.push_back(nums[i]); used[i]=true;
        backtrack(nums,cur,used);
        cur.pop_back(); used[i]=false;
    }
}

也可以加个start用swap来做。

 

Permutations II

先排序,方便跳过重复元素。和subsets不一样了,由于permutations没有start这个下标,每次递归都是重新从头开始的,判断跳过重复元素比较复杂。

如果当前元素使用过了,直接continue。如果当前元素和前一个元素相同,有两种情况:

第一种,前面的元素也用过,说明需要递归当前元素。如 [1,1,1]

第二种,前面元素没有用过。这说明前面元素已经递归完毕了,used又被设置为了false。我们无需再次递归当前元素了。

vector<vector<int>> res;
vector<vector<int>> permuteUnique(vector<int> &nums) {
    sort(nums.begin(),nums.end());
    vector<int> cur;
    vector<bool> used(nums.size(),false);
    backtrack(nums,cur,used);
    return res;
}
void backtrack(vector<int> &nums, vector<int> &cur, vector<bool> &used){
    if (cur.size()==nums.size()){ res.push_back(cur); return; }
    for (int i=0;i<nums.size();++i){
        if (used[i] || i>0 && nums[i]==nums[i-1] && !used[i-1]) continue;
        cur.push_back(nums[i]); used[i]=true;
        backtrack(nums,cur,used);
        cur.pop_back(); used[i]=false;
    }
}

 

Permutations 时间复杂度

每次函数 F(n) 都会调用n次 F(n-1)

T(n) = nT(n-1) = n(n-1)T(n-2) = ... = n!T(0)

如果考虑 res.push_back(cur) 是时间 O(n),那么总的时间复杂度为 O(n*n!)

Permutations 空间复杂度 

O(n) 递归深度最多n。

 

====================================================================

Combination Sum

由于元素可以重复元素,因此递归的时候,backtrack(nums,target-nums[i],cur,i),是递归 i 而不是 i+1

vector<vector<int>> res;
vector<vector<int>> combinationSum(vector<int>& nums, int target) {
    vector<int> cur;
    backtrack(nums,target,cur,0);
    return res;
}
void backtrack(vector<int>& nums, int target, vector<int> &cur, int start){
    if (target==0) {res.push_back(cur); return;}
    if (target<0) return;
    for (int i=start;i<nums.size();++i){
        cur.push_back(nums[i]);
        backtrack(nums,target-nums[i],cur,i);
        cur.pop_back();
    }
}

 

Combination Sum II

先排序,方便跳过重复元素。同一层递归中相同元素跳过。

vector<vector<int>> res;
vector<vector<int>> combinationSum2(vector<int>& nums, int target) {
    sort(nums.begin(),nums.end());
    vector<int> cur;
    backtrack(nums,target,cur,0);
    return res;
}
void backtrack(vector<int>& nums, int target, vector<int> &cur, int start){
    if (target<0) return;
    if (target==0) {res.push_back(cur); return;}
    for (int i=start;i<nums.size();++i){
        if (i>start && nums[i]==nums[i-1]) continue;
        cur.push_back(nums[i]);
        backtrack(nums,target-nums[i],cur,i+1);
        cur.pop_back();
    }
}

 

Combination Sum 时间复杂度

T(n) = T(n-1)+T(n-2)+...+T(1)+T(0) -> T(n) = O(2^n)

由于 target<=0 就直接return了,所以其实是比 O(2^n) 小的,但最坏情况的确会是 O(2^n)

如果考虑 res.push_back(cur) 是时间 O(n),那么总的时间复杂度为 O(n*2^n)

Combination Sum 空间复杂度 

O(n) 递归深度最多n。

 

 

References:

https://leetcode.com/problems/subsets/discuss/27281/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)

https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=117602&extra=&page=1

转载于:https://www.cnblogs.com/hankunyan/p/9944142.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值