Leetcode 51-N皇后

题解转载自liweiwei1419

题目描述

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

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

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
在这里插入图片描述

题解

回溯算法(深度优先遍历)
(基于行,列,对角线的集合做回溯)
先尝试画出递归树,以 4 皇后问题为例,画出的递归树如下:
在这里插入图片描述
搜索的过程蕴含了 剪枝 的思想。「剪枝」的依据是:题目中给出的 「N 皇后」 的摆放规则:1、不在同一行;2、不在同一列;3、不在同一主对角线方向上;4、不在同一副对角线方向上。
小技巧:记住已经摆放的皇后的位置

这里记住已经摆放的位置不能像 Flood Fill 一样,简单地使用 visited 布尔数组。放置的规则是:一行一行考虑皇后可以放置在哪一个位置上,某一行在考虑某一列是否可以放置皇后的时候,需要根据前面已经放置的皇后的位置。
由于是一行一行考虑放置皇后,摆放的这些皇后肯定不在同一行,为了避免它们在同一列,需要一个长度为 N 的布尔数组 cols,已经放置的皇后占据的列,就需要在对应的列的位置标注为 True。

考虑对角线(找规律)
下面我们研究一下主对角线或者副对角线上的元素有什么特性。在每一个单元格里写下行和列的 下标。

在这里插入图片描述
为了保证至少两个皇后不同时出现在 同一主对角线方向 或者 同一副对角线方向。检查策略是,只要「检测」到新摆放的「皇后」与已经摆放好的「皇后」冲突,就尝试摆放同一行的下一个位置,到行尾还不能放置皇后,就退回到上一行。

可以像全排列 used 数组那样,再为 「主对角线(Main diagonal)」 和 「副对角线(Sub diagonal)」 设置相应的 布尔数组变量,只要排定一个 「皇后」 的位置,就需要占住对应的位置。

编码
我们使用一个 1 到 4 的排列表示一个 4×4 的棋盘,例如:
在这里插入图片描述
得到一个符合要求的全排列以后,生成棋盘的代码就很简单了。

dfs:
1.参数:row,path
row代表遍历到了第几行,path代表当前的摆放序列,path中是每一行的皇后的纵坐标j
2.递归终止条件:row==n
3.如何递归到下一步:dfs(row+1,path)
4.如何回溯:
sub[row+j]=false;
main[row-j+n-1]=false;
col[j]=false;
path.removeLast();

class Solution {
    private int n;

    //记录某一列是否放置了皇后
    private boolean[] col;
    //记录主对角线上的单元格是否放置了皇后
    private boolean[] main;
    //记录副对角线上的单元格是否放置了皇后
    private boolean[] sub;
    private List<List<String>> res;

    public List<List<String>> solveNQueens(int n) {
        res = new ArrayList<>();
        if(n==0) return res;

        this.n = n;
        this.col = new boolean[n];
        //同一个方向上有2*n-1条对角线
        this.main = new boolean[2 * n - 1];
        this.sub = new boolean[2 * n - 1];
        LinkedList<Integer> path = new LinkedList<>();
        dfs(0, path);
        return res;
    }

    //row:  path
    private void dfs(int row,LinkedList<Integer> path){
        //深度优先遍历到下标为n,表示[0...n-1]已经填完,得到了一个结果
        if(row==n){
            List<String> board = convert2board(path);
            res.add(board);
            return;
        }
        //针对下标为row的每一列,尝试是否可以放置
        for(int j=0;j<n;j++){
            if(!col[j]&&!main[row-j+n-1]&&!sub[row+j]){
                path.addLast(j);
                col[j]=true;
                main[row-j+n-1]=true;
                sub[row+j]=true;

                dfs(row+1,path);

                sub[row+j]=false;
                main[row-j+n-1]=false;
                col[j]=false;
                path.removeLast();
            }
        }
    }

    private List<String> convert2board(LinkedList<Integer> path){
        List<String> board = new ArrayList<>();
        for(Integer num:path){
            StringBuilder row = new StringBuilder();
            row.append(".".repeat(Math.max(0,n)));
            row.replace(num,num+1,"Q");
            board.add(row.toString());
        }
        return board;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值