[代码随想录]回溯算法

回溯算法

1. 组合

77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

class Solution {
    
    
public:
   void backing(vector<vector<int>> &combine,vector<int> &v,int num,int n,int k){
    
    
       if(v.size() == k ){
    
    
           combine.push_back(v);
           return;
       }
       
       //优化
       //v.size =已经放进去的元素
       //k - v.size = 所需需要的元素个数
       //列表中剩余元素(n-i) >= 所需需要的元素个数(k - path.size())
       // i <= n - (k - v.size()) + 1,开始遍历,有个+1,因为包括起始位置
       for(int i = num;i <= n - (k - v.size()) + 1;i++){
    
    
           v.push_back(i);
           num++;
           backing(combine,v,num,n,k);
           v.pop_back();
       }
   }
   vector<vector<int>> combine(int n, int k) {
    
    
       vector<vector<int>> combine;
       vector<int> v;
       if(n < k)return combine;
       int num = 1;
       backing(combine,v,num,n,k);
       return combine;
   }
};

2.组合总和III

216.组合总和III

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

class Solution {
    
    
public:
    void backtracing(vector<vector<int>>& combin,vector<int>& v,int k,int n,int numstart,int sum){
    
    
        if(v.size() == k){
    
    
            if(sum == n){
    
    
                combin.push_back(v);
            }
            return;
        }

        for(int i = numstart;i <= 9 - (k - v.size()) + 1;i++){
    
    
            v.push_back(i);
            backtracing(combin,v,k,n,++numstart,sum + i);
            v.pop_back();
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
    
    
        vector<vector<int>>combin;
        vector<int>v;
        int sum = 0;
        for(int i = 1;i <= k;i++){
    
    
            sum += i;
        }

        if(sum > n){
    
    
            return combin;
        }
        else{
    
    
            backtracing(combin,v,k,n,1,0);
        }
        return combin;
    }
};

3.电话号码的字母组合

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
    
    
public:
    string s2 = "abc",s3 = "def",s4 = "ghi",s5 = "jkl",s6 = "mno",s7 = "pqrs",s8 = "tuv",s9 = "wxyz";
    
    void backtracing(string digits,string& s,vector<string>& combin,int startnum){
    
    
        if(s.size() == digits.size()){
    
    
            combin.push_back(s);
            return;
        }

        int num = digits[startnum] - '0';
        switch(num){
    
    
            case 2:{
    
    
                 for(int j = 0;j < s2.size();j++){
    
    
                    s.push_back(s2[j]);
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 3:{
    
    
                for(int j = 0;j < 3;j++){
    
    
                    s.push_back(s3[j]);
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 4:{
    
    
                for(int j = 0;j < 3;j++){
    
    
                    s += s4[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 5:{
    
    
                for(int j = 0;j < 3;j++){
    
    
                    s += s5[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 6:{
    
    
                for(int j = 0;j < 3;j++){
    
    
                    s += s6[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 7:{
    
    
                for(int j = 0;j < 4;j++){
    
    
                    s += s7[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 8:{
    
    
                for(int j = 0;j < 3;j++){
    
    
                    s += s8[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
            case 9:{
    
    
                for(int j = 0;j < 4;j++){
    
    
                    s += s9[j];
                    backtracing(digits,s,combin,startnum + 1);
                    s.pop_back();
                }
            }break;
        }
    }
    
    vector<string> letterCombinations(string digits) {
    
    
        vector<string>combin;
        string s;
        if(digits.size() == 0)return combin;
        backtracing(digits,s,combin,0);
        return combin;
    }
};

4. 组合总和

39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

class Solution {
    
    
public:
    void backtracing(vector<vector<int>>&combin,vector<int>&v,vector<int>&candidates,int target,int sum,int startnum){
    
    
        if(sum == target){
    
    
            combin.push_back(v);
            return;
        }
        if(sum > target || (target - sum) < candidates[startnum]){
    
    
            return;
        }

        for(int i = startnum;i < candidates.size();i++){
    
    
            v.push_back(candidates[i]);
            sum += candidates[i];
            backtracing(combin,v,candidates,target,sum,startnum);
            startnum++;
            sum -= candidates[i];
            v.pop_back();
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
    
    
        vector<vector<int>>combin;
        vector<int>v;
        sort(candidates.begin(),candidates.end());
        if(target < candidates[0]){
    
    
            return combin;
        }
        int startnum = 0;
        backtracing(combin,v,candidates,target,0,startnum);
        return combin;
    }
};

5.组合总和II

40.组合总和II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

class Solution {
    
    
private:
    vector<vector<int>>combin;
    vector<int>v;
public:
   
    void backtracing(vector<int>& candidates,int target,int sum,int startnum){
    
    
        if(sum == target){
    
    
            combin.push_back(v);
            return;
        }
        if(sum > target || startnum < candidates.size() &&(target - sum) < candidates[startnum]){
    
    
            return;
        }

        for(int i = startnum;i < candidates.size();i++){
    
    
            if(i > startnum && candidates[i] == candidates[i - 1] ){
    
    
                continue;
            }
            v.push_back(candidates[i]);
            sum += candidates[i];
            backtracing(candidates,target,sum,i + 1);
            sum -= candidates[i];
            v.pop_back();
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
    
    
        sort(candidates.begin(),candidates.end());
        if(target < candidates[0])return combin;
        backtracing(candidates,target,0,0);
        return combin;
    }
};

6.分割回文串

131.分割回文串

class Solution {
    
    
private:
    vector<vector<string>>part;
    vector<string>v;
public:
    bool pan(string& str){
    
    
        for(int i = 0,j = str.size() - 1;i < j;i++,j--){
    
    
            if(str[i] != str[j])return false;
        }
        return true;
    }
    void backtracing(string& s,int startnum){
    
    
        if(startnum >= s.size()){
    
    
            part.push_back(v);
            return;
        }
        for(int i = startnum;i < s.size();i++){
    
    
            string str = s.substr(startnum,i - startnum + 1);
            if(pan(str) == true){
    
    
                v.push_back(str);
                backtracing(s,i + 1);
                v.pop_back();
            }
            else{
    
    
                continue;
            }
        }
    }

    vector<vector<string>> partition(string s) {
    
    
        backtracing(s,0);
        return part;
    }
};

7.复原IP地址

93.复原IP地址

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

  • 例如:"0.1.2.201" "192.168.1.1"有效 IP 地址,但是 "0.011.255.245""192.168.1.312""[email protected]"无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

class Solution {
    
    
private:
    vector<string>restore;
public:
    //判断语句不要搞得花里胡哨,直接按照最简单来
    bool istrue(string& s,int start,int end){
    
    
        if (start > end) {
    
    
            return false;
        }
        if (s[start] == '0' && start != end) {
    
     // 0开头的数字不合法
            return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
    
    
            if (s[i] > '9' || s[i] < '0') {
    
     // 遇到非数字字符不合法
                return false;
            }
            num = num * 10 + (s[i] - '0');
            if (num > 255) {
    
     // 如果大于255了不合法
                return false;
            }
        }
        return true;
    }
    void backtracing(string& s, int startnum,int cutnum){
    
    
        if(cutnum == 3){
    
    
            if(istrue(s,startnum,s.size() - 1)){
    
    
                restore.push_back(s);
            }
            return;
        }
        for(int i = startnum;i < s.size();i++){
    
    
            if(istrue(s,startnum,i)){
    
    
                s.insert(s.begin() + i + 1,'.');
                cutnum++;
            }
            else{
    
    
                return;
            }
            backtracing(s,i + 2,cutnum);
            cutnum--;
            s.erase(s.begin() + i + 1);
        }
    }
    vector<string> restoreIpAddresses(string s) {
    
    
        string address;
        if(s.size() < 4)return restore;
        backtracing(s,0,0);
        return restore;
    }
};

8.子集

78.子集

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

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

class Solution {
    
    
private:
    vector<vector<int>>sub;
    vector<int>v;
public:
    void backtracing(vector<int>&nums,int start){
    
    
        if(v.size() > 0 && v[v.size() - 1] == nums[nums.size() - 1]){
    
    
            sub.push_back(v);
            return;
        }else sub.push_back(v);

        for(int i = start;i < nums.size();i++){
    
    
            v.push_back(nums[i]);
            backtracing(nums,i + 1);
            v.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
    
    
        backtracing(nums,0);
        return sub;
    }
};

9.子集II

90.子集II

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

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

class Solution {
    
    
private:
    vector<vector<int>>sub;
    vector<int>v;
public:
    void backtracing(vector<int>& nums,int startnum){
    
    
        sub.push_back(v);
        if(startnum >= nums.size())return;

        for(int i = startnum;i < nums.size();i++){
    
    
            if(i > startnum && nums[i] == nums[i - 1]){
    
    
                continue;
            }
            v.push_back(nums[i]);
            backtracing(nums,i + 1);
            v.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
    
    
        sort(nums.begin(),nums.end());
        backtracing(nums,0);
        return sub;
    }
};

10.递增子序列

491.递增子序列

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

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

class Solution {
    
    
private:
    vector<vector<int>>find;
    vector<int>v;
public:
    //用哈希表也可以
    void backtracing(vector<int>&nums,int startnum){
    
    
        if(v.size() > 1)find.push_back(v);
        if(startnum >= nums.size())return;
        vector<int>used(201,0);
        for(int i = startnum;i < nums.size();i++){
    
    
            if( v.size() > 0 && nums[i] < v.back() || used[nums[i] + 100] == 1){
    
    
                continue;
            }
            v.push_back(nums[i]);
            used[nums[i] + 100] = 1;
            backtracing(nums,i + 1);
            v.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
    
    
        backtracing(nums,0);
        return find;
    }
};

11.全排列

46.全排列

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

class Solution {
    
    
private:
    vector<vector<int>>per;
    vector<int>v;
    void backtracing(vector<int>& nums,vector<int>&used){
    
    
        if(v.size() == nums.size()){
    
    
            per.push_back(v);
            return;
        }
        for(int i = 0;i < nums.size();i++){
    
    
            if(used[i] == 1)continue;

            v.push_back(nums[i]);
            used[i] = 1;
            backtracing(nums,used);
            v.pop_back();
            used[i] = 0;
        }
    }
public:
    vector<vector<int>> permute(vector<int>& nums) {
    
    
        vector<int>used(nums.size(),0);
        backtracing(nums,used);
        return per;
    }
};

12.全排列 II

47.全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列

class Solution {
    
    
private:
    vector<vector<int>>per;
    vector<int>v;
    void backtracing(vector<int>& nums,vector<int>& used,int startnum){
    
    
        if(v.size() == nums.size()){
    
    
            per.push_back(v);
            return;
        }
        int used2[21] = {
    
    0};
        for(int i = 0;i < nums.size();i++){
    
    
            if(used[i] == 1 || used2[nums[i] + 10] == 1)continue;
            v.push_back(nums[i]);
            used[i] = 1;
            used2[nums[i] + 10] = 1;
            backtracing(nums,used,startnum + 1);
            v.pop_back();
            used[i] = 0;
        }
    }
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
    
    
        vector<int> used(nums.size(),0);
        backtracing(nums,used,0);
        return per;
    }
};

13.重新安排行程

332.重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"]["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

class Solution {
    
    
private:
    vector<vector<string>>find;
    vector<string>v;
    //得定义静态,否则报错
    static bool cmp(const vector<string>&s1,const vector<string>&s2){
    
    
        return s1[1] < s2[1];
    }
    void backtracing(vector<vector<string>>& tickets,vector<int>&used,string s){
    
    
        if(find.size() == 1)return;//有一个就返回
        if(v.size() == tickets.size() + 1){
    
    
            find.push_back(v);
        }
        for(int i = 0;i < tickets.size() ;i++){
    
    
            if(used[i] == 1 || tickets[i][0] != s)continue;      
            v.push_back(tickets[i][1]);
            used[i] = 1;
            backtracing(tickets,used,tickets[i][1]);
            v.pop_back();
            used[i] = 0;
        }
 
    }
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
    
    
        vector<int>used(tickets.size(),0);
        v.push_back("JFK");
        sort(tickets.begin(),tickets.end(),cmp);//太强了,自己写了总是有错误,这个方法太巧妙l
        backtracing(tickets,used,"JFK");
        return find[0];
    }
};

*14. N皇后

51. N皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

class Solution {
    
    
private:
    vector<vector<string>>solve;
    vector<string>v;

    bool isnot(int hang,int lie,int n){
    
    
        for(int i = 0;i < hang;i++){
    
    
            if(v[i][lie] == 'Q')return false;
        }//上面
        for(int i = 0;i < hang;i++){
    
    
            if(i >= hang - lie && v[i][lie + i - hang] == 'Q'){
    
    
                return false;
            }
        }//左上
        for(int i = 0;i < hang;i++){
    
    
            if(i >= lie + hang - n && v[i][lie + hang - i] == 'Q'){
    
    
                return false;
            }
        }//右上
        return true;
    }

    void backtracing(int n,int hang){
    
    
        if( v.size() == n){
    
    
            solve.push_back(v);
            return;
        }
        for(int i = 0;i < n;i++){
    
    
            string s(n,'.');
            if(hang == 0 || isnot(hang,i,n)){
    
    
                s[i] = 'Q';
                v.push_back(s);
                backtracing(n,hang + 1);
                v.pop_back();
            }
        }
    }
public:
    vector<vector<string>> solveNQueens(int n) {
    
    
        backtracing(n,0);
        return solve;
    }
};

*15. 解数独

37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

class Solution {
    
    
private:
/*
 如果for循环中没有if (backtracking(board)) return true;会怎样? 即不判断返回值,只进行递归。
若只去掉这句返回值判断,其他返回代码保持不变。 那么即使找到正确的一个解,最底层返回true,上一个层对返回值无处理,会直接进入下一个for循环数字。 直至所有数字遍历结束,会进行回溯,并返回上一层。 最终结果相当于填好了数独,又给全部撤销了。
*/
    bool istrue(vector<vector<char>>& board,int hang ,int lie,char num){
    
    
        for(int i = 0;i < 9;i++){
    
    
            if(board[hang][i] == num)return false;
        }//行
        for(int i = 0;i < 9;i++){
    
    
            if(board[i][lie] == num)return false;
        }//列
        int starthang = (hang / 3) * 3;
        int startlie = (lie / 3) * 3;
        for(int i = starthang;i < starthang + 3;i++){
    
    
            for(int j = startlie;j < startlie + 3;j++){
    
    
                if(board[i][j] == num)return false;
            }
        } 
        return true;
    }
    bool solve(vector<vector<char>>& board) {
    
    
        for(int i = 0;i < 9;i++){
    
    
            for(int j = 0;j < 9;j++){
    
    
                if(board[i][j] != '.')continue;
                for(int k = '1';k <= '9';k++){
    
    
                    if(istrue(board,i,j,k)){
    
    
                    board[i][j] = k;
                    if(solve(board))return true;
                    board[i][j] = '.';
                    }
                }
                return false;
            }
        }  
        return true;   
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
    
    
        solve(board);
    }
};

猜你喜欢

转载自blog.csdn.net/m0_53953432/article/details/130469273
今日推荐