您的位置:首页 > 产品设计 > UI/UE

[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:

[
[".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