回溯刷题记录

导语

因学习全排列,却不懂原理和做法,因此将LeetCode中笔者所有尝试过的回溯题目归并到此随笔下,方便日后复盘学习。

例题

子集Ⅰ

题目

78. 子集 - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res, bool used[], int idx){
        if (tmp.size() != 0){
            res.emplace_back(tmp);
        }
        for(int i = idx; i < nums.size(); i++) {
            if (!used[i]){
                used[i] = true;
                tmp.emplace_back(nums[i]);
                dfs(nums,tmp,res,used,i + 1);
                used[i] = false;
                tmp.pop_back();
            }
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
        res.emplace_back(tmp);
        bool used[10];
        memset(used, 0, 10);
        dfs(nums,tmp,res,used,0);
        return res;
    }
};

子集II

题目

90. 子集 II - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res, bool used[], int idx){
        if (tmp.size() != 0){
            res.emplace_back(tmp);
        }
        for(int i = idx; i < nums.size(); i++) {
            // 判断此路径是否已遍历
            if (used[i] || (i > idx && nums[i] == nums[i - 1])){
                continue;
            }
            used[i] = true;
            tmp.emplace_back(nums[i]);
            dfs(nums,tmp,res,used,i + 1);
            used[i] = false;
            tmp.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
        res.emplace_back(tmp);
        bool used[10];
        memset(used, 0, 10);
        sort(nums.begin(), nums.end());
        dfs(nums,tmp,res,used,0);
        return res;
    }
};

全排列

题目

46. 全排列 - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(vector<vector<int>>& res, vector<int>& nums, vector<int>& tmp, bool used[]){
        if (tmp.size() == nums.size()) {
            res.push_back(tmp);
            return;
        }
        for (int i = 0; i < nums.size(); i++){
            if(!used[i]){
                used[i] = true;
                tmp.push_back(nums[i]);
                dfs(res, nums, tmp,used);
                used[i] = false;
                tmp.pop_back();
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
        bool used[7];
        memset(used,0,7);
        dfs(res, nums, tmp, used);
        return res;
    }
};

全排列II

题目

47. 全排列 II - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(vector<int>& nums,vector<int> &tmp, vector<vector<int>>& res, bool used[], int idx){
        if (tmp.size() == nums.size()) {
            res.push_back(tmp);
            return;
        }
        for(int i = 0; i < nums.size(); i++){
            // 剪枝:判断是否为未使用的重复数字
            if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) continue;
            used[i] = true;
            tmp.push_back(nums[i]);
            dfs(nums,tmp,res,used,idx+1);
            used[i] = false;
            tmp.pop_back();
        }
    } 
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> tmp;
        bool used[10];
        sort(nums.begin(), nums.end());
        dfs(nums,tmp,res,used, 0);
        return res;
    }
};

组合

题目

77. 组合 - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res, bool used[], int flag, int k){
        if(tmp.size() == k) {
            res.push_back(tmp);
            return;
        }
        // 组合 不可以从后面开始取
        // 全排列,可以先取后面的数字
        for(int i = flag; i < nums.size(); i++) {
            if (!used[i] ) {
                used[i] = true;
                tmp.push_back(nums[i]);
                dfs(nums,tmp,res,used,i + 1, k);
                used[i] = false;
                tmp.pop_back(); 
            }
        }
    }
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> res;
        bool used[n];
        memset(used, 0, n);        
        vector<int> nums;
        for (int i = 1; i <= n; i++) nums.push_back(i);
        vector<int> tmp;
        dfs(nums,tmp,res,used, 0,k);
        return res;
    }
};

组合总和

题目

39. 组合总和 - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(int target, int sum, int idx, vector<int>& candidates,vector<int>& tmp, vector<vector<int>>& res) {
        if(sum == target) {
            res.push_back(tmp);
            return;
        }
        // 按序记录
        for (int i = idx; i < candidates.size(); i++) {
            if (sum + candidates[i] <= target) {
                tmp.push_back(candidates[i]);
                dfs(target, sum + candidates[i], i, candidates,tmp, res);
                tmp.pop_back();
            }
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> tmp;
        // 排序数组,使得数组有序。
        sort(candidates.begin(),candidates.end());
        dfs(target,0, 0, candidates,tmp, res);
        return res;
    }
};

组合总和II

题目

40. 组合总和 II - 力扣(LeetCode)

代码

class Solution {
public:
    void dfs(int target, int sum, int idx, vector<int>& candidates,vector<int>& tmp, vector<vector<int>>& res) {
        if(sum == target) {
            res.push_back(tmp);
            return;
        }
        // 按序记录
        for (int i = idx; i < candidates.size(); i++) {
            // 剪枝:如果第二次出现,直接跳过本循环。
            if (i > idx && candidates[i] == candidates[i - 1]) continue;
            if (sum + candidates[i] <= target) {
                tmp.push_back(candidates[i]);
                // 下一次迭代取i+1,此次的重复出现,不会被剪枝
                dfs(target, sum + candidates[i], i + 1, candidates,tmp, res);
                tmp.pop_back();
            }
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> tmp;
        // 排序数组,使得数组有序。
        sort(candidates.begin(),candidates.end());
        dfs(target,0, 0, candidates,tmp, res);
        return res;
    }
};

组合总和III

题目

[216. 组合总和 III - 力扣(LeetCode)](https://leetcode.cn/problems/combination-sum-ii/)

代码

class Solution {
public:
    void dfs(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res, int n, int k, int idx, int sum){
        if (sum == n && tmp.size() == k) {
            res.push_back(tmp);
            return;
        }

        for(int i = idx; i < 10; i++){
            if (sum + nums[i] <= n) {
                tmp.push_back(nums[i]);
                dfs(nums,tmp,res,n,k,i + 1,sum + nums[i]);
                tmp.pop_back();
            }
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        vector<vector<int>> res;
        vector<int> tmp;
        vector<int> nums;
        for(int i = 1; i < 10; i++) nums.push_back(i);
        dfs(nums,tmp,res,n,k,0,0);
        return res;
    }
};

组合总和IV

题目

377. 组合总和 Ⅳ - 力扣(LeetCode)

超时代码

class Solution {
public:
    void dfs(vector<int>& nums, vector<int>& tmp, vector<vector<int>>& res, int target, int sum){
        if(sum == target) {
            res.push_back(tmp);
            return;
        }
        for(int i = 0; i < nums.size(); i++){
            if (sum + nums[i] <= target) {
                tmp.push_back(nums[i]);
                dfs(nums,tmp,res, target,sum + nums[i]);
                tmp.pop_back();
            }
        }
    }
    int combinationSum4(vector<int>& nums, int target) {
        vector<vector<int>> res;
        vector<int> tmp;
        sort(nums.begin(), nums.end());
        dfs(nums,tmp,res,target,0);
        return res.size();
    }
};

总结

回溯:是一种思想,通过遍历各个单位后,采用递归的方式,在原先的基础上继续寻找,直到找到所有符合要求结果。在回溯的过程中,存在不符合条件的路径,此时需要采用剪枝对回溯路径进行修改。同时回溯本质是暴力的枚举。其原理在于每一次回溯后,必须进行恢复现场,犹如回到起点。如对一个数字加上1和减1,最终得出结果不变。

剪枝:跳过不符合条件的回溯路径。

另外大多时候,问题如果有解便是最大的幸运,即使回溯本身性能太差。


未完待续。。。

posted @ 2022-05-29 21:58  DiiD  阅读(69)  评论(0编辑  收藏  举报