[LeetCode]N-Queens
2016-10-21 19:16
218 查看
Question
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:
本题难度Hard。
1、原始版
【复杂度】
时间 O(N^3) 空间 O(N^5)
【思路】
每一列只会有一个Queen存在。那么我们从左往右,通过DFS来遍历各种情况。
【注意】
38行对board进行了复制,防止recursion导致的side effect。
【代码】
2、升级版
【复杂度】
时间 O(N^3) 空间 O(N)
【思路】
原始版太浪费空间了。在这里用一个一维数组
【代码】
3、optimize版
【复杂度】
时间 O(N^2) 空间 O(N)
【思路】
该方法的思路和上面两个方法一样,区别在于:
之前我们判断一个皇后是否冲突,是遍历一遍当前皇后排列的列表,看每一个皇后是否冲突。这里,我们用三个集合来保存之前皇后的信息,就可以O(1)时间判断出皇后是否冲突。
三个集合分别是行集合,用于存放有哪些行被占了,主对角线集合,用于存放哪个右上到左下的对角线被占了,副对角线集合,用于存放哪个左上到右下的对角线被占了。如何唯一的判断某个点所在的主对角线和副对角线呢?
我们发现,两个点的行号加列号的和相同,则两个点在同一条主对角线上。两个点的行号减列号的差相同,则两个点再同一条副对角线上。
【注意】
主对角线
【代码】
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.."] ]
本题难度Hard。
1、原始版
【复杂度】
时间 O(N^3) 空间 O(N^5)
【思路】
每一列只会有一个Queen存在。那么我们从左往右,通过DFS来遍历各种情况。
'\0'代表初始值 'Q'代表Queen '.'代表不能放Queen
【注意】
38行对board进行了复制,防止recursion导致的side effect。
【代码】
public class Solution { public List<List<String>> solveNQueens(int n) { //require List<List<String>> ans=new ArrayList<>(); if(n<=0) return ans; //create board char[][] board=new char ; //invariant dfs(board,n,0,ans); //ensure return ans; } private char[][] copyBorad(char[][] oldBoard){ char[][] newBoard=new char[oldBoard.length][oldBoard[0].length]; for(int i=0;i<oldBoard.length;i++) for(int j=0;j<oldBoard[0].length;j++) newBoard[i][j]=oldBoard[i][j]; return newBoard; } private void dfs(char[][] board,int n,int step,List<List<String>> ans){ //base case if(step>n-1){ List<String> list=new ArrayList<String>(); for(char[] chars:board){ for(int i=0;i<n;i++){ chars[i]=(chars[i]=='\0')?'.':chars[i]; } list.add(new String(chars)); } ans.add(list); return; } for(int i=0;i<n;i++){ if(board[i][step]!='.'){ //copy old board char[][] newBoard=copyBorad(board); newBoard[i][step]='Q'; for(int j=1;j<n-step;j++){ newBoard[i][step+j]='.'; if(i-j>=0) newBoard[i-j][step+j]='.'; if(i+j<n) newBoard[i+j][step+j]='.'; } dfs(newBoard,n,step+1,ans); } } } }
2、升级版
【复杂度】
时间 O(N^3) 空间 O(N)
【思路】
原始版太浪费空间了。在这里用一个一维数组
nqueens来表示二维棋盘上皇后的位置。一维数组中每一个值的下标代表着对应棋盘的列,每一个值则是那一列中皇后所在的行。这样我们可以只对一个一维数组进行深度优先搜索,来找出对于每一列,我们的皇后应该放在哪一行。在下一轮搜索之前,我们先检查一下新构成的数组是否是有效的,这样可以剪掉不必要的分支。
【代码】
public class Solution { public List<List<String>> solveNQueens(int n) { //require List<List<String>> ans=new ArrayList<>(); if(n<=0) return ans; //create int[] nqueens=new int ; //invariant dfs(nqueens,n,0,ans); //ensure return ans; } private boolean isValid(int[] nqueens,int step){ for(int i=0;i<step;i++){ if(nqueens[i]==nqueens[step]||Math.abs(nqueens[i]-nqueens[step])==(step-i)) return false; } return true; } private void dfs(int[] nqueens,int n,int step,List<List<String>> ans){ //base case if(step>n-1){ List<String> list=new ArrayList<String>(); for(int num:nqueens){ StringBuilder sb=new StringBuilder(); for(int i=0;i<num;i++) sb.append("."); sb.append("Q"); for(int i=num+1;i<n;i++) sb.append("."); list.add(sb.toString()); } ans.add(list); return; } for(int i=0;i<n;i++){ nqueens[step]=i; if(isValid(nqueens,step)) dfs(nqueens,n,step+1,ans); } } }
3、optimize版
【复杂度】
时间 O(N^2) 空间 O(N)
【思路】
该方法的思路和上面两个方法一样,区别在于:
之前我们判断一个皇后是否冲突,是遍历一遍当前皇后排列的列表,看每一个皇后是否冲突。这里,我们用三个集合来保存之前皇后的信息,就可以O(1)时间判断出皇后是否冲突。
三个集合分别是行集合,用于存放有哪些行被占了,主对角线集合,用于存放哪个右上到左下的对角线被占了,副对角线集合,用于存放哪个左上到右下的对角线被占了。如何唯一的判断某个点所在的主对角线和副对角线呢?
我们发现,两个点的行号加列号的和相同,则两个点在同一条主对角线上。两个点的行号减列号的差相同,则两个点再同一条副对角线上。
【注意】
主对角线
row + col,副对角线
row - col
【代码】
public class Solution { List<List<String>> res = new LinkedList<List<String>>();; Set<Integer> rowSet = new HashSet<Integer>(); Set<Integer> diag1Set = new HashSet<Integer>(); Set<Integer> diag2Set = new HashSet<Integer>(); public List<List<String>> solveNQueens(int n) { helper(new LinkedList<Integer>(), n, 0); return res; } public void helper(LinkedList<Integer> tmp, int n, int col){ if(col == n){ List<String> one = new LinkedList<String>(); for(Integer num : tmp){ StringBuilder sb = new StringBuilder(); for(int j = 0; j < num; j++){ sb.append('.'); } sb.append('Q'); for(int j = num + 1; j < n; j++){ sb.append('.'); } one.add(sb.toString()); } res.add(one); } else { // 对于列col,看皇后应该放在第几行 for(int row = 0; row < n; row++){ int diag1 = row + col; int diag2 = row - col; // 如果三条线上已经有占据的皇后了,则跳过该种摆法 if(rowSet.contains(row) || diag1Set.contains(diag1) || diag2Set.contains(diag2)){ continue; } // 用回溯法递归求解 tmp.add(row); rowSet.add(row); diag1Set.add(diag1); diag2Set.add(diag2); helper(tmp, n, col + 1); diag2Set.remove(diag2); diag1Set.remove(diag1); rowSet.remove(row); tmp.removeLast(); } } } }
参考
[Leetcode] N-Queens N皇后相关文章推荐
- leetcode笔记:N-Queens
- Leetcode N-Queens
- leetcode-51-N-Queens
- leetcode n-queens
- LeetCode之N-Queens
- [LeetCode]N-Queens
- [Leetcode 91] 51 N-Queens
- leetcode N-Queens & N-Queens II
- 【LeetCode】51. N-Queens
- LeetCode---N-Queens
- LeetCode --- 51. N-Queens
- leetcode 51. N-Queens
- leetcode — n-queens
- N-Queens -- LeetCode
- 2017.11.8 LeetCode N皇后问题 - 51. N-Queens - 52....
- LeetCode 50 N-Queens
- LeetCode 51. N-Queens
- LeetCode 51. N-Queens
- [Leetcode] 51. N-Queens 解题报告
- LeetCode题解:N-Queens I and II