算法题之N皇后

N皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

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

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

'Q' : 代表皇后,'.' : 代表空位。

其中,1 <= n <= 9。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

解体思路

这道题关键的点在于,同一行、同一列、同一斜线上,不能有相同的皇后。

从整体上来看

  1. 我们在第一行第一列放置一个皇后
  2. 接着我们遍历到第二行,然后从左往右,排除不能放置皇后的位置(第一列,第二列),在第三列上放置皇后
  3. 然后遍历到下一行
  4. 遍历当前行,排除不能放置皇后的列,在后面的列放置皇后,如果已经遍历完全部的行,我们记录答案。如果行遍历完了或者没有可放置的列了,就进行5的操作;如果行没有遍历完,我们继续3的操作
  5. 接着我们回溯到上一行,然后继续4的操作

从细节上来看

  1. 我们需要在n×n的棋盘上,放置n个皇后,所以可以用长度为n的一维数组存放皇后,其中元素值代表的是每个皇后放置的位置,最后作为答案时,把数组调整成题目需要的结构,然后返回。
  2. 因为相同的列上,只能放置一个皇后,我们用长度为n的一维数组记录,如果放置了皇后,我们把这个位置标记成1。
  3. 然后在斜上排除位置,斜着的位置分为左斜和右斜
  4. 我们先看左斜,如果在(row, col)的位置上放置了皇后,那么(row + 1, col - 1)的位置上不能放置皇后了,同样的(row + 2, col - 2)的位置上也不能放置皇后,我们可以用row + col来代表这些位置,标记成了1代表位置已经被放置了,那么记录的数据长度需要多大呢,最大的位置是(n - 1)  + (n - 1) = 2 * n - 2。数组的大小就设置为2 * n - 2么?注意这里有个细节点,数组的下表是从0开始的,所以如果我们想标记到2 * n - 2 的位置,那么数组的长度需要设置为2 * n - 1,数组应该初始化为new int[2 * n - 1]。
  5. 我们再看右斜,如果在(row, col)的位置上放置了皇后,那么(row + 1, col + 1)的位置上不能放置皇后了,同样的(row + 2, col + 2)的位置上也不能放置皇后,也就是需要把row + col的位置上标记成1,row + col + 2的位置上也要标记成1,以此类推......这样的话,我们就需要用二维组来记录了。那么能不能就像左斜一样,用一维数组就可以记录了呢?这个时候我们需要找一下规律了,如果我们用row - col作为位置,是不是就可以表示(row + 1, col + 1)、(row + 2, col + 2)了?所以我们用row - col来记录,考虑到-n + 1 <= row - col <= n - 1,数组下标不能为负数,所以row - col需要整体的偏移n - 1,也就是 0 <= row - col + n - 1 <= 2 * n - 2,最后数组的长度也为2 * n - 1。

具体代码如下:

class Solution {

    int n;
    int[] rows;
    int[] hills;
    int[] dales;
    int[] queens;

    List<List<String>> ans = new ArrayList();

    public List<List<String>> solveNQueens(int n) {
        this.n = n;
        rows = new int[n];
        hills = new int[2 * n - 1];
        dales = new int[2 * n - 1];
        queens = new int[n];

        backtrack(0);

        return ans;
    }

    public void backtrack(int row){
        
        for(int col = 0; col < n; col++){
            if(isNotUnderAttack(row, col)){ // 不会被攻击
                placeQueen(row, col); // 放置皇后
                
                if(row + 1 == n)addSolution(); // 放置到了最后一行就记录答案
                else backtrack(row + 1); // 否则继续递归

                removeQueen(row, col); // 移除皇后
            }
        }
    }

    private boolean isNotUnderAttack(int row, int col){
        int res = rows[col] + hills[row - col + n - 1] + dales[row + col];
        return (res == 0)? true : false;
    }

    private void placeQueen(int row, int col){
        queens[row] = col;
        rows[col] = 1;
        hills[row - col + n - 1] = 1;
        dales[row + col] = 1;
    }

    private void removeQueen(int row, int col){
        queens[row] = 0;
        rows[col] = 0;
        hills[row - col + n - 1] = 0;
        dales[row + col] = 0;
    }

    private void addSolution(){
        
        List<String> solution = new ArrayList<String>();
        for(int i = 0; i < n; i++){
         
            int col = queens[i];
            StringBuilder sb = new StringBuilder();
            for(int j = 0; j < col; j++)sb.append(".");
            sb.append("Q");
            for(int k = 0; k < n - col - 1; k++)sb.append(".");

            solution.add(sb.toString());
        }
        ans.add(solution);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值