经典的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("============="); } }