经典的8皇后,递归回溯可解。同时还学了StringBuilder里面一个setCharAt()很方便的方法。
package Level4;
import java.util.ArrayList;
import java.util.Arrays;
/**
*
* 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.
http://www.leetcode.com/wp-content/uploads/2012/03/8-queens.png
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.."]
]
*
*/
public class S51 {
public static void main(String[] args) {
System.out.println(solveNQueens(4));
}
public static ArrayList<String[]> solveNQueens(int n) {
ArrayList<String[]> ret = new ArrayList<String[]>();
int[] queenList = new int[n];
placeQueen(queenList, 0, n, ret);
return ret;
}
// 递归回溯8皇后,关键记录下到达了哪一行了
public static void placeQueen(int[] queenList, int row, int n, ArrayList<String[]> ret){
// Base Case, 已经完成任务了
if(row == n){
StringBuilder[] sol = new StringBuilder[n];
// 对数组内每一个对象都要new出其对象
for(int i=0; i<n; i++){
sol[i] = new StringBuilder();
for(int j=0; j<n; j++){
sol[i].append(".");
}
}
// 在相应的地方放置queen
for(int i=0; i<n; i++){
sol[i].setCharAt(queenList[i], 'Q');
}
String[] ss = new String[n];
for (int i=0; i<n; i++) {
ss[i] = sol[i].toString();
}
ret.add(ss);
return;
}
// 开始这一行的查找
// 遍历第row行的所有列,测试哪一个位置是安全的
for(int col=0; col<n; col++){
if(isSafe(queenList, row, col)){
queenList[row] = col;
placeQueen(queenList, row+1, n, ret);
}
}
}
// 判断是否坐标(row,col)的位置是安全的(检查行,列,正反对角线)
// queenList里面存放行,列坐标pair,即queenList[row] = col
public static boolean isSafe(int[] queenList, int row, int col){
for(int preRow=0; preRow<row; preRow++){
int preCol = queenList[preRow];
if(preRow == row){ // 理论上不必检查,因为preRow是总是小于row的
return false;
}
if(preCol == col){ // 检查是否在同一列
return false;
}
if(row-preRow == col-preCol){ // 反对角线
return false;
}
if(preRow+preCol == row+col){ // 正对角线
return false;
}
}
return true;
}
}
这道题因为要求出全部可能,所以rec函数就没有返回值。如果只要求出一种,返回值就设为boolean,在里面当检查到一种情况为true时就直接返回,只有当检查完所有可能性后才return false (比如sudoku solver)
public class Solution { public static List<String[]> solveNQueens(int n) { List<String[]> list = new ArrayList<String[]>(); StringBuilder[] sb = new StringBuilder[n]; String s = ""; for(int i=0; i<n; i++) { s += "."; } for(int i=0; i<n; i++) { sb[i] = new StringBuilder(s); } rec(list, sb, n, 0); return list; } public static void rec(List<String[]> list, StringBuilder[] sb, int n, int level) { if(level == n) { String[] ss = new String[n]; for(int i=0; i<n; i++) { ss[i] = sb[i].toString(); } list.add(ss); return; } for(int j=0; j<n; j++) { sb[level].setCharAt(j, 'Q'); if(isValid(sb)) { rec(list, sb, n, level+1); } sb[level].setCharAt(j, '.'); } } public static boolean isValid(StringBuilder[] sb) { // print(sb); for(int x1=0; x1<sb.length; x1++) { for(int x2=x1+1; x2<sb.length; x2++) { int y1 = sb[x1].indexOf("Q"); int y2 = sb[x2].indexOf("Q"); if(y1 == -1 || y2 == -1) { continue; } if(y1 == y2 || Math.abs(x1-x2) == Math.abs(y1-y2)) { return false; } } } return true; } public static void print(StringBuilder[] sb) { for(int i=0; i<sb.length; i++) { for(int j=0; j<sb[i].length(); j++) { System.out.print(sb[i].charAt(j)); } System.out.println(); } System.out.println("============="); }}