day30 回溯问题 重新安排行程 N皇后 解数独

题目1:332 重新安排形成

题目链接:重新安排行程

对题目的理解

题目2:51 N皇后

题目链接:N皇后

对题目的理解

将皇后放置在不同行,不同列,不同对角线

!!!注意检查不同列时,一定要是纵向检查,不能是横向检查,因为递归的时候,一定是新的一行进行,这新的一行肯定是没有皇后的,那么这么横向比较毫无意义,所以进行纵向比较,判断这一列有没有皇后。

回溯法

回溯三部曲

i)递归函数参数

定义全局变量二维数组result来记录最终结果。

参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。

ii)递归终止条件

当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。

iii)单层搜索的逻辑

递归深度就是row控制棋盘的行,每一层里for循环的i控制棋盘的列,一行一列,确定了放置皇后的位置。

每次都是要从新的一行的起始位置开始搜,所以都是从0开始。

代码

class Solution {
public:
    vector<vector<string>> result;
    void backtracking(vector<string>& cheeseboard,int n,int row){
        //终止条件
        if(row==n){
            result.push_back(cheeseboard);
            return;
        }
        //单层搜索逻辑
        for(int i=0;i<n;i++){//列
            if(isvalid(row,i,cheeseboard,n)){
                cheeseboard[row][i] = 'Q';
                backtracking(cheeseboard,n,row+1);//递归,因为要向下一行递归
                cheeseboard[row][i] = '.';//回溯
            }
        }
    }
    bool isvalid(int row,int col,vector<string>& cheeseboard,int n){
        //检查每一行的同一列有没有,只有这样检查才是争取的检查
        //不能检查同一行的每一列,这样每一比较意义,因为每一行都是新的一行,都没有元素
        for(int i=0;i<row;i++)
            if(cheeseboard[i][col]=='Q') return false;
        //检查45度角
        for(int i=row-1,j=col-1;i>=0 && j>=0;i--,j--)
            if(cheeseboard[i][j]=='Q') return false;
        //检查135度角
        for(int i=row-1,j=col+1;i>=0 && j<n;i--,j++)
            if(cheeseboard[i][j]=='Q') return false;
        return true;
    }  
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> cheeseboard(n,std::string(n,'.'));
        backtracking(cheeseboard,n,0);
        return result;

    }
};
  • 时间复杂度: O(n!)
  • 空间复杂度: O(n)

题目3: 37 解数独

题目链接:解数独

对题目的理解

1-9在每一行出现一次,每一列出现一次,在3*3格内出现一次

注意本题与N皇后的区别

N皇后是每一行每一列只放一个皇后,需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置;

本题是棋盘的每一个位置都要放置一个数字,所以树形结构要比N皇后更宽更深

回溯法

回溯三部曲

i)递归函数以及参数

递归函数的返回值需要是bool类型,因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值

ii)递归终止条件

本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。

不用终止条件会不会死循环?

递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件

iii)递归单层搜索逻辑

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

判断棋盘是否合法

同行是否重复,同列是否重复,9宫格里是否重复

代码

class Solution {
public:
    bool backtracking(vector<vector<char>>& board){
        //单层搜索逻辑,二维递归
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                if(board[i][j]=='.'){//遇到空格
                    for(char k='1';k<='9';k++){
                        if(isvalid(board,i,j,k)){
                            board[i][j]=k;
                           // return true;
                            bool result = backtracking(board);
                            if(result==true) return true;
                            board[i][j] = '.';  //回溯
                        }
                    }
                    return false;//9个数都尝试了,都不行
                }  
            }
        }
        return true;//空格都被填满了,没有返回false说明找到了一个棋盘
    }
    bool isvalid(vector<vector<char>>& board,int row,int col,char val){
        for(int i=0;i<9;i++){
            if(board[row][i]==val) return false;//判断一行中每一列是否重复
        }
        for(int j=0;j<9;j++){
            if(board[j][col]==val) return false;//判断同一列中每一行是否重复
        }
        int startrow = (row/3)*3;//除以3是看这一行位于第几个九宫格内,再乘以3是看位于九宫格内的第几行,这个就是求解元素所在九宫格的第一个行
        int startcol = (col/3)*3;//求解元素所在九宫格的第一列
        for(int i=startrow;i<startrow+3;i++){//判断9宫格内的元素是否重复,startrow+3代表九宫格的最后一行
            for(int j=startcol;j<startcol+3;j++){//startcol+3代表九宫格的最后一列
                if(board[i][j]==val) return false;
            }
        }
        return true;
    }
    void solveSudoku(vector<vector<char>>& board) {
        //注意这是一个void类型
        backtracking(board);
        


    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值