Leetcode51.N皇后(回溯法)

N皇后问题的经典不言而喻,不多哔哔,直接上

审题

 (祝大家审题健康哈哈哈哈哈烂梗)

给定输入n----一个自然数

目标是给出一个让n个皇后们无法互相攻击的方案

也就是说,在一张二维n行n列矩阵中,同列同行同斜线都只能有一个皇后Q

给定的返回也是一个二维数组,其中皇后“Q”和空位"."可以通过字符串的基本操作来进行插入等等。

基本的思路就是上面了👆

分析

把它划给回溯法大类是有原因的。

正向思考:1.选中第i行的某个位置

                  2.选中第i+1行的某个位置,判断是否合法

                  3.合法继续,不合法更换位置,如果遍历了第i+1行的全部位置都不合法,则重选上一行(第i行)位置

这样理解是不是就很想组合问题了!

代码逻辑

就非常抱歉,这里还是参考我自己习惯的回溯法代码框架,有点死记硬背那味儿,但是逻辑上是有道理的

class Solution {
    //1.结果数组直接用主函数返回值类型就对了
    List<List<String>> res = new ArrayList<>();

    
    //某种意义上的主函数
    public List<List<String>> solveNQueens(int n) {

    }

    //回溯函数,传入参数是行数n,以及标记row,表示当前在第几行
    void backtracking(n,int row){


    }
    //判断是否合法的函数
    boolean isValid(){

    }
}

主函数

在主函数的操作其实非常直观。

定义出一个新的棋盘,大小为n*n,类型是字符数组,并初始化用“.”填充

这里的几个基本的操作可以记下来,比如Arrays里的很多函数经常用得上(fill,sort等等)

 //某种意义上的主函数
    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for(char[] c : chessboard){
            Arrays.fill(c,".");
        }
        backtracking(n,0);
        return res;
    }

因为我们需要操作的过程本质上是在对一个二维字符型数组做修改,但是注意返回类型中的每条“路径”是字符串类型的一维数组,所以这里给chessboard专门设置一个转换类型的方法(当然你有好方法也可以自己在初始化的时候就定义好相同类型,在递归时给出相应操作也可以,我这里基本上用的是随想录的源码)

//数据类型转换工具,二维字符数组转换为String类型的一维数组
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }

判断合法的函数

由主函数中定义的棋盘不难看出,我们肯定还得是在棋盘内判断,并遵守以下规则:

1.同行不能出现两个皇后

2.同列不能出现两个皇后

3.同斜线不出现两个皇后

同时由于回溯的过程就是一行一行铺,在处理每行时,遍历所有列,并在此时判断当前位置下的皇后是否合法

    //判断是否合法的函数
    boolean isValid(int row,int col,char[][] chessboard,int n){
        for(int i =0;i < row;i++){
            //同列有Q则返回错误
            if(chessboard[i][col]=='Q'){
                return false;
            }
        }
        //检查右上到左下斜线
         for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        //检查左上右下斜线
        // 检查135度对角线
        for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }

回溯过程

有了以上的铺垫,回溯过程的思路就相对来说清晰明了:

1.以行为递进单位,判断在当前行,选中第i列时是否合法

2.若合法,则进入下一行;不合法则继续遍历第i+1列

3.若当前行所有列都不合法,则进行回溯

4.若遍历到最后一行,并找到合法方案,则将当前方案纳入结果集,再继续回溯。

所以递归出口就是,当前行已经走到了最后一行:

if(row == n){
     res.add(Array2List(chessboard));
     return;
       }

回溯函数的循环体内:

1.以列为递进单位

2.判断合法则修改chessboard;不合法则continue(默认就是进入下一次循环,也就是下一列)

3.递归至下一行

4.遇到出口,则回溯

for(int col = 0;col < n; col++){
            if(isValid(row,col,chessboard,n)){
                chessboard[row][col]='Q';
                backtracking(n,row+1,chessboard);
                chessboard[row][col] = '.';
            }
        }

以上是代码的全部模块,贴个所有代码:

class Solution {
    //1.结果数组直接用主函数返回值类型就对了
    List<List<String>> res = new ArrayList<>();
    char [][] chessboard;
    //某种意义上的主函数
    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for(int i =0;i<n;i++){
            for(int j = 0;j <n;j++){
                chessboard[i][j]='.';
            }
        }
        backtracking(n,0,chessboard);
        return res;
    }

    //回溯函数,传入参数是行数n,以及标记row,表示当前在第几行
    void backtracking(int n,int row,char[][]chessboard){
        if(row == n){
            res.add(Array2List(chessboard));
            return;
        }
        for(int col = 0;col < n; col++){
            if(isValid(row,col,chessboard,n)){
                chessboard[row][col]='Q';
                backtracking(n,row+1,chessboard);
                chessboard[row][col] = '.';
            }
        }

    }
    //数据类型转换工具,二维字符数组转换为String类型的一维数组
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }

    //判断是否合法的函数
    boolean isValid(int row,int col,char[][] chessboard,int n){
        for(int i =0;i < row;i++){
            //同列有Q则返回错误
            if(chessboard[i][col]=='Q'){
                return false;
            }
        }
        //检查右上到左下斜线
         for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        //检查左上右下斜线
        // 检查135度对角线
        for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
}

总结:

1.N皇后的遍历思维符合回溯递归逻辑

2.如果是有这样的固有框架思维,需要考虑的仅仅是一些细节:

        1)判断合法

        2)过程集合的数据类型

        3)回溯过程中的思路要清晰

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值