回溯

本文详细介绍了回溯算法的基本模板,并通过LeetCode上的多个经典问题(如组合、分割回文串、复原IP地址、子集和递增子序列等)展示了如何使用回溯法解决这类问题。文章深入浅出地解析了每个问题的解题思路,包括剪枝优化等技巧,旨在帮助读者理解并掌握回溯算法在实际问题中的应用。
摘要由CSDN通过智能技术生成


回溯

回溯模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

组合问题

LeetCode77. 组合:从n个无重复数中选取k个数的组合,不可以重复选取,组合不可重复;

LeetCode216. 组合总和Ⅲ:从1~9中选取k个和为n的数的组合,不可以重复选取,组合不可重复;

LeetCode39. 组合总和:从一个无重复元素的数组中选取和为target的数的组合,可以重复选取,组合不可重复;

LeetCode40. 组合总和Ⅱ:从有重复元素的数组中选取和为target的数的组合,元素不可以重复选取,组合不可重复。

LeetCode77. 组合

题目描述:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

链接:https://leetcode-cn.com/problems/combinations

class Solution {
public:
    vector<vector<int>> ans;  //存放符合条件结果的集合
    vector<int> path;         //存放符合条件的结果

    void backtracking(int n, int k, int startindex) {
        //终止条件
        if (path.size() == k) {
            ans.push_back(path);
            return;
        }

        for (int i = startindex; i <= n; i++) {
            path.push_back(i);           //处理节点
            backtracking(n, k, i + 1);   //递归
            path.pop_back();             //回溯,撤销处理结果
        }
    }

    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return ans;
    }
};

剪枝优化:

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

    void backtracking(int n, int k, int startindex) {
        if (path.size() == k) {
            ans.push_back(path);
            return;
        }

        for (int i = startindex; i <= n - (k - path.size()) + 1; i++) {    //剪枝优化
            path.push_back(i);
            backtracking(n, k, i + 1);
            path.pop_back();
        }
    }

    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return ans;
    }
};

分割问题

LeetCode131. 分割回文串

题目描述:给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

链接:https://leetcode.cn/problems/palindrome-partitioning

class Solution {
public:
    vector<vector<string>> ans;
    vector<string> path;
    void dfs(string& s, int index) {
        if (index >= s.size()) {
            ans.push_back(path);
            return;
        }
        for (int i = index; i < s.size(); i++) {
            string temp = s.substr(index, i - index + 1);
            if (isPalindrome(temp)) {
                path.push_back(temp);
                dfs(s, i + 1);
                path.pop_back();
            }
        }
    }

    bool isPalindrome(const string& s) {
        int left = 0;
        int right = s.size() - 1;
        while (left <= right) {
            if (s[left] == s[right]) {
                left++;
                right--;
            }else return false;
        }
        return true;
    }

    vector<vector<string>> partition(string s) {
        dfs(s, 0);
        return ans;
    }
};

LeetCode93. 复原IP地址

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

链接:https://leetcode.cn/problems/restore-ip-addresses

class Solution {
public:
    vector<string> ans;
    string path;
    void dfs(string& s, int index, int num) {
        //已经完成一组分割
        if (num == 3) {
            string cur = s.substr(index, s.size() - index + 1);
            if (isValid(cur)) {
                path += cur;
                ans.push_back(path);
                path.erase(path.end() - cur.size(), path.end());
            }
            return;
        }

        for (int i = index; i < s.size(); i++) {
            string temp = s.substr(index, i - index + 1);
            if (isValid(temp)) {
                path += temp;
                path += ".";
                num++;
                dfs(s, i + 1, num);
                path.erase(path.end() - 1, path.end());
                path.erase(path.end() - temp.size(), path.end());
                num--;
            }
        }
    }

    bool isValid(const string& s) {
        if (s.size() >= 4 || s.size() == 0) return false;
        if (s.size() == 3) {
            if (s[0] == '0') return false;
            if ((s[0] - '0') * 100 + (s[1] - '0') * 10 + s[2] - '0' > 255) return false;
        }
        if (s.size() == 2) {
            if (s[0] == '0') return false;
        }
        return true;
    }

    vector<string> restoreIpAddresses(string s) {
        dfs(s, 0, 0);
        return ans;
    }
};

子集问题

LeetCode78. 子集

题目描述:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集不能包含重复的子集。你可以按 任意顺序 返回解集。

链接:https://leetcode.cn/problems/subsets

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    void dfs(vector<int>& nums, int index) {
        ans.push_back(path);
        if (index >= nums.size()) return;

        for (int i = index; i < nums.size(); i++) {
            path.push_back(nums[i]);
            dfs(nums, i + 1);
            path.pop_back();
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(nums, 0);
        return ans;
    }
};

LeetCode90. 子集Ⅱ

题目描述:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

链接:https://leetcode.cn/problems/subsets-ii

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    void dfs(vector<int>& nums, int index) {
        ans.push_back(path);
        if (index >= nums.size()) return;

        for (int i = index; i < nums.size(); i++) {
            if (i > index && nums[i] == nums[i - 1]) continue;  //遇到重复元素则跳过
            path.push_back(nums[i]);
            dfs(nums, i + 1);
            path.pop_back();
        }

    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        dfs(nums, 0);
        return ans;
    }
};

LeetCode491. 递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

链接:https://leetcode.cn/problems/increasing-subsequences

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    void dfs(vector<int>& nums, int index) {
        if (path.size() > 1) ans.push_back(path);
        if (index >= nums.size()) return;

        unordered_set<int> st;
        for (int i = index; i < nums.size(); i++) {
            if (st.find(nums[i]) == st.end() && (path.size() == 0 || (path.size() > 0 && nums[i] >= path.back()))) {
                st.insert(nums[i]);
                path.push_back(nums[i]);
                dfs(nums, i + 1);
                path.pop_back();
            }else continue;
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        dfs(nums, 0);
        return ans;
    }
};

排列问题

LeetCode46. 全排列

题目描述:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

链接:https://leetcode.cn/problems/permutations

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    void dfs(vector<int>& nums, vector<bool> used) {
        if (path.size() == nums.size()) {
            ans.push_back(path);
            return;
        }

        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == false) {
                path.push_back(nums[i]);
                used[i] = true;
                dfs(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(), false);
        dfs(nums, used);
        return ans;
    }
};

LeetCode47. 全排列Ⅱ

题目描述:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

链接:https://leetcode.cn/problems/permutations-ii

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;
    void dfs(vector<int>& nums, vector<bool>& used) {
        if (path.size() == nums.size()) {
            ans.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue;
            if (used[i] == false) {
                used[i] = true;
                path.push_back(nums[i]);
                dfs(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        dfs(nums, used);
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值