51. N-Queens

1 题目理解

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space, respectively.

n皇后问题

输入:整数n
输出:不同的解决方法
规则:n个皇后不能在同一行、同一列、同一对角线上,反对角线也不可以。
在这里插入图片描述

2 回溯

2.1 直观解法

这道题目最直接的解法是,遍历每个位置,在这个位置判断是否可以放皇后,如果能放,就有两种选择:放、不放。如果不能放,则向前递归。这个思路和数独题目类似。不同之处是还需要考虑对角线和反对角线。(以下内容来自力扣
在这里插入图片描述
对于对角线上的元素特点是横坐标-纵坐标的值相同。
例如对角线(0,0) (1,1),(2,2),横坐标-纵坐标=0
对角线(0,4)(1,5)(2,6),横坐标-纵坐标=-4
用数组int[] diagnol 表示对角线是否冲突。可以用差的绝对值,也可以i-j+n来将横坐标-纵坐标放在一个合理的数组下标范围内。

在这里插入图片描述
反对角线的特征是 横坐标+纵坐标 值固定。
例如反对角线(0,2)(1,1)(2,0),横坐标+纵坐标=2
反对角线(0,5)(1,4)(2,3),横坐标+纵坐标=5
用数组int[] antiDiagnol 表示反对角线是否冲突。

class Solution {
    private List<List<String>> answer;
    private int n;
    private int[][] board;//表示第i行第j列是不是有皇后
    private int[] rows;
    private int[] cols;
    private int[] diagnol;
    private int[] antiDiagnol;
    public List<List<String>> solveNQueens(int n) {
        answer = new ArrayList<List<String>>();
        this.n = n;
        board = new int[n][n];
        rows = new int[n];
        cols = new int[n];
        diagnol = new int[2*n];
        antiDiagnol = new int[2*n];
        dfs(0,0,0);
        return answer;
    }
    
    private void dfs(int row,int col,int queueCount){
        if(col == n){
            if(queueCount==n){
                List<String> list = new ArrayList<String>();
                for(int i=0;i<n;i++){
                    String val = "";
                    for(int j=0;j<n;j++){
                        val+=(board[i][j]==1?"Q":".");
                    }
                    list.add(val);
                }

                answer.add(list);
            }
            
        }else{
            int nextRow = (row+1)%n;
            int nextCol = (nextRow==0?col+1:col);
                
            if(canPutQueue(row,col)){
                put(row,col);
                dfs(nextRow,nextCol,queueCount+1);
                clear(row,col);
            }
           
            dfs(nextRow,nextCol,queueCount);

        }
    }
    
    private void put(int row,int col){
        board[row][col]=1;
        rows[row]=1;
        cols[col] = 1;
        diagnol[row-col+n]=1;
        antiDiagnol[row+col]=1;
    }
    
    private void clear(int row,int col){
        board[row][col]=0;
        rows[row]=0;
        cols[col] = 0;
        diagnol[row-col+n]=0;
        antiDiagnol[row+col]=0;
    }
        
    private boolean canPutQueue(int i,int j){
        return rows[i]==0 && cols[j]==0 && diagnol[i-j+n]==0 && antiDiagnol[i+j]==0;
    }
}

因为过程中有放和不放的操作,所以需要记录最终放了多少个皇后。所以有queueCount==n的判断。
时间复杂度:有 n 2 n^2 n2个位置,每个位置有9种可能,所以最终时间复杂度 n 18 n^{18} n18

2.2 按行遍历

因为每个皇后最后一行上只能有一个,所以可以先按行遍历,在这一行一定要放一个。在处理每一行的时候可以枚举放在[0,n-1]的某一列。

class Solution {
    private List<List<String>> answer;
    private int n;
    private int[][] board;//表示第i行第j列是不是有皇后
    private int[] cols;
    private int[] diagnol;
    private int[] antiDiagnol;
    public List<List<String>> solveNQueens(int n) {
        answer = new ArrayList<List<String>>();
        this.n = n;
        board = new int[n][n];
        cols = new int[n];
        diagnol = new int[2*n];
        antiDiagnol = new int[2*n];
        dfs(0);
        return answer;
    }
    
    private void dfs(int row){
        if(row == n){
            List<String> list = new ArrayList<String>();
            for(int i=0;i<n;i++){
                String val = "";
                for(int j=0;j<n;j++){
                    val+=(board[i][j]==1?"Q":".");
                }
                list.add(val);
            }
            answer.add(list);
        }else{
            for(int col=0;col<n;col++){
                if(canPutQueue(row,col)){
                    put(row,col);
                    dfs(row+1);
                    clear(row,col);
                }
            }
        }
    }
    
    private void put(int row,int col){
        board[row][col]=1;
        cols[col] = 1;
        diagnol[row-col+n]=1;
        antiDiagnol[row+col]=1;
    }
    
    private void clear(int row,int col){
        board[row][col]=0;
        cols[col] = 0;
        diagnol[row-col+n]=0;
        antiDiagnol[row+col]=0;
    }
        
    private boolean canPutQueue(int i,int j){
        return cols[j]==0 && diagnol[i-j+n]==0 && antiDiagnol[i+j]==0;
    }
}

时间复杂度: O ( n ! ) O(n!) O(n!)第一行有n种放法,第二行有n-1种放法,第三行有n-2种…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值