N-Queens N皇后问题@LeetCode

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值