leetcode刷题 ---- 回溯算法

1: 78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

class Solution {
public:
//  回溯算法的核心:就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」
//  在决策树上dfs
    vector<vector<int>> res;
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> track;
        backtrace(nums,0,track);
        return res;
    }

    void backtrace(vector<int> &nums, int start, vector<int> &track){
        //每次记录结果。
        res.push_back(track);

        for(int i = start; i<nums.size(); ++i){  //因为是子集,那么每次需要从下一个元素接着做选择
            //做选择
            track.push_back(nums[i]);

            //从下一个元素接着做选择
            backtrace(nums,i+1,track);

            //撤销选择
            track.pop_back();
        }

    }
};

2:77. 组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> combine(int n, int k) {
        vector<int> track;
        backtrace(n,k,1,track);

        return res;
    }

    void backtrace(int n, int k, int start,vector<int> &track){
        
        if(track.size() == k){
            res.push_back(track);
            return;
        }

        for(int i = start; i<=n; i++){
            track.push_back(i);  //做选择

            backtrace(n,k,i+1,track);  //组合与顺序无关,所以需要在下一个元素上接着选择

            track.pop_back();  //撤销选择
        }

    }
};

3:46. 全排列

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

class Solution {
public:
vector<vector<int>> res;
    vector<vector<int>> permute(vector<int>& nums) {
        set<int> mem;  
        vector<int> tmp;
        backtrack(nums,mem,tmp);

        return res;
    }

    void backtrack(vector<int> &nums, set<int> &mem, vector<int> &tmp){
        if(mem.size() == nums.size())
        {
            //用集合作为返回的值具有很大的问题,因为set是有序的,这样写之后所有的结果都是一样的
            //res.push_back(vector<int>(mem.begin(),mem.end()))
            res.push_back(tmp);
            //cout<<endl;
            return;
        }
        for(int i = 0; i<nums.size(); ++i){
            
            
            if(mem.count(nums[i])) continue;
            //做选择
            mem.insert(nums[i]);
            tmp.push_back(nums[i]);

            backtrack(nums,mem,tmp);

            //撤销选择
            mem.erase(nums[i]);
            tmp.pop_back();
            //cout<<*mem.begin()<<" "<<*mem.end()<<endl;
        }
        return;
    }
};

4:37. 解数独

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

class Solution {
public:
string nums = "123456789";
    void solveSudoku(vector<vector<char>>& board) {
        backtrace(board,0,0);
    }

    //判断字符是否合法
    bool isvalid(vector<vector<char>> &board,int r, int c, char n){
        //判断每一行或者每一列是否有chr数字
        for(int k = 0; k<9; k++){
            if(board[r][k] == n) return false;
            if(board[k][c] == n) return false;
        }
        //判断小方格里面有没有相同的数字
        int x = r/3, y = c/3;
        for(int k = 0; k<3; ++k)
            for(int m = 0; m<3; ++m){
                if(board[3*x+k][3*y+m] == n) return false;
            }

        return true;
    } 

    //用bool作为返回值,因为只需要找到一个可行解,利用返回值来使得找到之后就退出
    bool backtrace(vector<vector<char>> &board, int r, int c){
        int m = 9, n = 9;

        if(c == n){  //如果列越界,那么从下一行开始
            return backtrace(board,r+1,0);
        }
        //r越界,说明遍历完了
        if(r == m) return true;

        for(int i = r; i<m; ++i){
            for(int j = c; j<n; ++j){

                if(board[i][j] != '.') return backtrace(board,i,j+1);  //如果已经填好了,那么不用我们穷举数字了,这里不能用continue。。。
                //设想,如果某一行的最后一个元素不是'.',如果continue的话,那么将会从下一行的c列开始,而不是第0列


                //做选择,对于每一个位置,尝试着找一个字符填入进去
                for(char ch = '1'; ch <= '9'; ch++){
                    if(!isvalid(board,i,j,ch)) continue;  //不合法的数字

                    board[i][j] = ch;

                    if(backtrace(board,i,j+1))  //按照行递增找,如果j+1超出了边界,那么从i+1开始
                        return true;

                    board[i][j] = '.';
                }
                //穷举完 1~9 还没有true,此路不通
                return false;
            }
        }
        return false;
    }
};

5:22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

class Solution {
public:
    vector<string> res;
    vector<string> generateParenthesis(int n) {
        string track;
        help(n,track,0,0,0);

        return res;
    }
    // l:左括号个数 r:右括号次数
    // sum:每次加一个左括号,sum++, 每加一个右括号 sum--
    void help(int n,string track,int l, int r, int sum){
        //base case
        if(sum < 0 || l>n || r>n) return;
        if(l == n && r==n && sum == 0){
            res.push_back(track);
            return;
        }
        
        for(int i = 0; i<2; ++i){
            string tmp = track;
            if(i == 0){  //选择左括号
                track += '(';
                help(n,track,l+1,r,sum+1);
            }else{  //选择右括号
                track += ')';
                help(n,track,l,r+1,sum-1);
            }
            track  = tmp;  //撤销选择
        }//for
    }
};

6:51. N 皇后

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

class Solution {
public:
     vector<vector<string>> res;
    vector<vector<string>> solveNQueens(int n) {
        //初始化一个全是 '.' 的字符串列表
        string s;
       for(int i = 0; i<n; ++i)
            s +='.';
        vector<string> board(n,s);
        help(board,n,0);
        return res;
    }

  //判断对应位置是否是合法的,这里需要注意一个问题:
  //1.只需要检查列是否有Q即可
  //2.对角线有两条:左上方和右上方
    bool isvalid(vector<string> &board, int r, int c, int n){

        //检查列
        for(int i = 0; i<r; ++i){
            if(board[i][c] == 'Q') return false;
        }
        //判断左上方是否有Q
        int tmpr = r, tmpc = c;
        while(tmpr>=0 && tmpc >=0){
            if(board[tmpr][tmpc] == 'Q') return false;
            tmpr--; tmpc--;
        }
        //判断右上方是否有Q
        tmpr = r; tmpc = c;
        while(tmpr>=0 && tmpc<n){
            if(board[tmpr][tmpc] == 'Q') return false;
            tmpr--; tmpc++;
        }
        return true;
    }

    void help(vector<string> &board, int n,int r){

        //base case
        if(r == n){
            res.push_back(board);
            return;
        }
        for(int j = 0; j<n; ++j){  //某一列
            if(!isvalid(board,r,j,n)) continue;  //如果不合法跳过

            board[r][j] = 'Q'; //选择

            help(board,n,r+1);  //下一行

            board[r][j] = '.';  //撤销选择
        }
    }
};

7:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值