【leetCode】 - N皇后

94 阅读2分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

题目

 n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

解析

其实跟数独问题有些类似,因此我们先来思考:

  • 如何解决这个问题?

事实上我们需要考虑的地方在于:

  • 每行,每列,两条对角线不能有重复

因此我们需要记录上一次填到哪里了,此处我们第一直觉当然是使用预计的nxn数组a来记录,预计是这样的:

 if(check){
     paint(a,x,y)
     //doNext
     unpaint(a,x,y)
 }

确定这个位置可以填之后,我们直接将相关不能填的都给赋值,然后执行下一次递归;递归结束后,我们将a复原。这样做的好处是:我们下一次检查,只要检查对应格子是否已被赋值就可以了。

但这个a的复原,如果a是二维数组的话是很难做的,原因如下:

  • 因为我们不清楚我们复原的格子,和原来其他的格子上面的是否有相交,因此还需要做额外的工作来检查其他的格子。

而且,注意到我们只关心那些填了数的格子,并且出于关系:

  • NxN的棋盘放n个皇后

因此我们知道:

  • 每行有且仅有一个皇后

因此我们使用一维数组rec[n],来记录我们之前已经填过的数。

  • 我们从前往后进行遍历,我们需要检查:对角线和列上的数即可,因为我们知道我们在每一行上填的数都是唯一的。

代码

 public static List<List<String>> solveNQueens(int n) {
         List<List<String>> res = new ArrayList<>();
         int[] rec = new int[n];
         Arrays.fill(rec,-20);
         test(rec,0,res,n);
         return res;
 }
 ​
 private static void test(int[] rec, int curLine, List<List<String>> res,int total) {
     if(curLine == total){
         List<String> cur = new ArrayList<>();
         for (int i = 0; i < rec.length; i++) {
             StringBuilder sb = new StringBuilder();
             for (int i1 = 0; i1 < total; i1++) {
                 sb.append(i1==rec[i]?"Q":".");
             }
             cur.add(sb.toString());
         }
         res.add(cur);
         return;
     }
 ​
     for (int i = 0; i < total; i++) {
         if(check(rec,curLine,i)){
             rec[curLine] = i;
             test(rec,curLine+1,res,total);
             rec[curLine] = -20;
         }
     }
 ​
 }
 ​
 private static boolean check(int[] rec,int x,int y){
     for (int i = 0; i <= x; i++) {
         if(rec[x-i]== y ||rec[x-i] == y-i || rec[x-i] == y+i) return false;
     }
     return true;
 }

执行用时:2 ms, 在所有 Java 提交中击败了93.97%的用户

内存消耗:38.6 MB, 在所有 Java 提交中击败了65.71%的用户