51. N-Queens
题目:
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.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ]
链接: http://leetcode.com/problems/n-queens/
题解:
又一道卡了很久的题。 N皇后问题,经典的NP-Complete。常见思路是dfs + backtracking。这里表面是暴力遍历所有的nn种可能,但backtracking的pruning可以减少决大部分的case。 因为皇后不可以处于同一行或列,所以每个结果肯定是n的一个排列,可以用一个数组queens来记录从行0到行n - 1中,不同queen的列坐标。用一个变量pos来控制行的增加以及进入下一层dfs。
Time Complexity - O(nn), Space Complexity - O(n) <- 递归栈的深度
public class Solution { List<List<String>> res = new ArrayList<>(); public List<List<String>> solveNQueens(int n) { if(n <= 0) return res; int[] queens = new int[n]; // queens must be a permutation of n trySolveNQueens(queens, 0); return res; } private void trySolveNQueens(int[] queens, int pos) { int len = queens.length; if(pos == len) addSolution(queens); else { for(int i = 0; i < len; i++) { queens[pos] = i; if(isBoardValid(queens, pos)) trySolveNQueens(queens, pos + 1); } } } private boolean isBoardValid(int[] queens, int pos) { for(int i = 0; i < pos; i++) { if(queens[i] == queens[pos]) // column conflicts return false; else if(Math.abs(queens[pos] - queens[i]) == Math.abs(i - pos)) // diagonal conflicts return false; } return true; } private void addSolution(int[] queens) { ArrayList<String> list = new ArrayList<>(); int len = queens.length; for(int i = 0; i < len; i++) { //loop through rows StringBuilder sb = new StringBuilder(); for(int j = 0; j < len; j++) { // loop through cols if(queens[i] == j) sb.append('Q'); else sb.append('.'); } list.add(sb.toString()); } res.add(list); } }
二刷:
Java:
二刷又卡了这道题,看了google的NQueens专题网站才知道有很多很好的解法。
这里我们用最普通的brute force。从头开始分析:
- 输入是int n,输出要求所有 n x n范围内的NQueens的合理解。我们把主要面对的问题分解,一步一步建立不同的子问题块
- 首先我们有题目给定的solveNQueens method。
- 在这个方法里我们主要定义一个结果集res,
- 判断基本的边界条件
- 建立一个int[n]数组queens。建立这个数组的目的是从数组的index来保存Queen的行坐标,queens[i]表示数组的列坐标,这样做的好处是我们可以不用check同一行内是否有重复的Queen
- 调用trySolveQueens(res, queens, 0)
- 返回结果
- 接下来我们需要一个dfs helper,这个method我定义叫trySolveNQueens
- 假如pos == queens.length,这时候说明之前的 0 ~ n - 1歌queen都放置完毕并且位置合理,这时候我们可以把结果加入结果集中
- 否则我们尝试在pos这一行的每一列放置Queen,假如放置完Queen以后board依然合理,则我们进入到下一层dfs,尝试在下一行的不同位置里放置Queen
- 一个辅助判断当前board是否valid的method isBoardValid
- 我们只用验证从0到当前的position pos之前的Queens
- 不需要验证行
- 验证是否有两个Queen存在同一列上 queens[i] == queens[pos]
- 验证对角线Math.abs(queens[i] - queens[pos]) == Math.abs(i - pos);
- 假如一切都合理,返回true
- 我们只用验证从0到当前的position pos之前的Queens
- 一个输出结果的method - collectSolution, 把记录下来的结果用题目要求的方式加入到结果集中
Time Complexity - O((n x n)n x n), Space Complexity - O(n2)
public class Solution { public List<List<String>> solveNQueens(int n) { List<List<String>> res = new ArrayList<>(); if (n <= 0) { return res; } int[] queens = new int[n]; trySolveNQueens(res, queens, 0); return res; } private void trySolveNQueens(List<List<String>> res, int[] queens, int pos) { if (pos == queens.length) { collectSolution(res, queens); } else { for (int i = 0; i < queens.length; i++) { queens[pos] = i; if (isBoardValid(queens, pos)) { trySolveNQueens(res, queens, pos + 1); } } } } private boolean isBoardValid(int[] queens, int pos) { for (int i = 0; i < pos; i++) { if (queens[i] == queens[pos]) { return false; } if (Math.abs(queens[i] - queens[pos]) == Math.abs(i - pos)) { return false; } } return true; } private void collectSolution(List<List<String>> res, int[] queens) { List<String> sol = new ArrayList<>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < queens.length; i++) { sb.setLength(0); for (int j = 0; j < queens.length; j++) { if (j != queens[i]) { sb.append('.'); } else { sb.append('Q'); } } sol.add(sb.toString()); } res.add(sol); } }
另外一种比较慢的方法,传递整个n x n数组,判断下一步是否可以走。
Time Complexity - O((n x n)n x n), Space Complexity - O(n2)
public class Solution { public List<List<String>> solveNQueens(int n) { List<List<String>> res = new ArrayList<>(); if (n <= 0) { return res; } for (int i = 0; i < n; i++) { canSolveNQueens(res, new int[n][n], 0, i); } return res; } private boolean canSolveNQueens(List<List<String>> res, int[][] board, int row, int col) { if (row == board.length - 1) { board[row][col] = 1; addSolution(res, board); board[row][col] = 1; return false; } board[row][col] = 1; for (int i = 0; i < board.length; i++) { if (isBoardValid(board, row + 1, i)) { if (!canSolveNQueens(res, board, row + 1, i)) { board[row + 1][i] = 0; } } } return false; } private boolean isBoardValid(int[][] board, int row, int col) { int n = board.length; if (!isValidRange(board, row, col)) { return false; } for (int i = 0; i < board.length; i++) { if (board[i][col] == 1 || board[row][i] == 1) { return false; } } for (int i = 0; i < n; i++) { if (isValidRange(board, row - i, col + i) && board[row - i][col + i] == 1) return false; if (isValidRange(board, row - i, col - i) && board[row - i][col - i] == 1) return false; } return true; } private boolean isValidRange(int[][] board, int x, int y) { return x >= 0 && x < board.length && y >= 0 && y < board.length; } private void addSolution(List<List<String>> res, int[][] board) { List<String> solution = new ArrayList<>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < board.length; i++) { sb.setLength(0); for (int j = 0; j < board[0].length; j++) { if (board[i][j] == 1) { sb.append('Q'); } else { sb.append('.'); } } solution.add(sb.toString()); } res.add(solution); } }
Reference:
http://www.algorist.com/
https://developers.google.com/optimization/puzzles/queens?hl=en#python
http://introcs.cs.princeton.edu/java/home/
http://www.geeksforgeeks.org/backtracking-set-3-n-queen-problem/
http://code.geeksforgeeks.org/po2i2q
https://leetcode.com/discuss/24717/comparably-concise-java-code
https://leetcode.com/discuss/63710/ac-python-76-ms-iterative-backtracking-solution
https://leetcode.com/discuss/49179/fast-short-and-easy-understand-python-solution-11-lines-76ms
https://leetcode.com/discuss/35128/accepted-solution-use-backtracking-bitmask-easy-understand
https://sites.google.com/site/nqueensolver/home/algorithm-results