Leetcode日记:51&52.N皇后问题

Leetcode日记:51&52.N皇后问题

题目

The n-queens puzzle is the problem of placing n queens on an n×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.

Example:

Input: 4
Output: [
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.

题目分析

这就是这次回溯问题的最终难题————经典的八皇后问题,题目主要的要求就是在摆N个皇后之后,皇后之间不能相互碰到。
这也要求我们利用回溯思想解决问题,当然不用回溯也是可以的,但是回溯的精妙之处在于其简洁,明了。让我们看看代码是如何实现的吧。

代码

class Solution {
    List<List<String>> result = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        boolean[] visited = new boolean[n];
        //2*n-1个斜对角线
        boolean[] dia1 = new boolean[2*n-1];
        boolean[] dia2 = new boolean[2*n-1];
        
        fun(n, new ArrayList<String>(),visited,dia1,dia2,0);
        
        return result;
    }
    
    private void fun(int n,List<String> list,boolean[] visited,boolean[] dia1,boolean[] dia2,int rowIndex){
        if(rowIndex == n){
            result.add(new ArrayList<String>(list));
            return;
        }
        
        for(int i=0;i<n;i++){
            //这一行、正对角线、反对角线都不能再放了,如果发现是true,停止本次循环
            if(visited[i] || dia1[rowIndex+i] || dia2[rowIndex-i+n-1])
                continue;
            
            //init一个长度为n的一维数组,里面初始化为'.'
            char[] charArray = new char[n];
            Arrays.fill(charArray,'.');
            
            charArray[i] = 'Q';
            String stringArray = new String(charArray);
            list.add(stringArray);
            visited[i] = true;
            dia1[rowIndex+i] = true;
            dia2[rowIndex-i+n-1] = true;

            fun(n,list,visited,dia1,dia2,rowIndex+1);

            //reset 不影响回溯的下个目标
            list.remove(list.size()-1);
            charArray[i] = '.';
            visited[i] = false;
            dia1[rowIndex+i] = false;
            dia2[rowIndex-i+n-1] = false;
        }
    }
    
}

代码分析

回溯思想

这道题的回溯思想和之前我们两道题基本一致,并没有过于难的地方:

结构:

  • 递归(迭代):这道题本身就是DFS问题,首先从第一行第一个开始,试着放一个皇后,接下来把相关信息放入,继续迭代(层数加一,rowIndex+1),当发现满足条件时存入数组并返回。
  • 条件:当放入的皇后数量等于目标数便可存入,当无解后会自动结束。在每一次布局时,共同维护着三个Boolean数组,来判断是否皇后会打架。(详细见下文)
  • 结束:当一次迭代执行完毕之后,不管这个方法成功与否,都把当前得到的排布的最后一行删掉list.remove(list.size()-1),并初始化这一行,并且还原Boolean数组,重新安排这一行的下一种布局(回溯)。

关于打架的判断

上文提到,每一次布局都会有与之对应的三个Boolean数组,这个数组便是检测在这一列、副对角线,主对角线上的皇后是否产生打架,这个规则不容易想,首先,我们以数组的形式来模拟棋盘:

[0,0][0,1][0,2][0,3][0,4]
[1,0][1,1][1,2][1,3][1,4]
[2,0][2,1][2,2][2,3][2,4]
[3,0][3,1][3,2][3,3][3,4]
[4,0][4,1][4,2][4,3][4,4]

我们来找一下:
visited[i] 是与之对应的列,这个很显然。
dia1[rowIndex+i]说的是副对角线,可以观察到副对角线上,行(rowIndex)与列(i)之是一样的。
dia2[rowIndex-i+n-1]说的是副对角线,仔细观察发现主对角线上,行(rowIndex)与列(i)之是一样的,但是为了避免出现数组索引为负数的情况,代码特意在后面+n-1

关于回溯时的状态重置

回溯状态重置这个问题已经是老生常谈了,但是仍然要引起重视,这是很容易遗漏的一点。

首先,回溯之前,要把之前迭代的不符合要求或者已经存入的上一层内容清空重置(谨记只是上一层内容),反映到此题上就是

list.remove(list.size()-1);
charArray[i] = '.';

相比之前几道题已经讲过很多次了。

还有就是要把判断状态表重置,反映在此题就是

visited[i] = false;
dia1[rowIndex+i] = false;
dia2[rowIndex-i+n-1] = false;

52题简要说明

这道题比第一个简单很多,只要求输出有多少种摆法。只需要在原看来代码的基础上删去不必要的List<List<String>> result,然后添加一个int型变量,得到满足条件的结果就+1就可以了。

更多回溯算法

更多关于回溯算法的问题请转到回溯算法标签

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值