输入一个N,找出所有在N行N列的棋盘摆放N个皇后的方法。要找出所有的解,是一个经典的使用回溯法的例子。都在注释里了:
public class NQueen { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNext()) { int N = sc.nextInt(); //假设输入的N都是合法的,这里省略了一些检查条件 int []a = new int[N]; //a[i]表示第i行摆放的列数 printQueenR(0,N,a); //递归算法,从第0行开始找放置位置 //printQueen(a,N); //非递归就用这行 } return ; } // (递归) 这个方法是确定第 k 行的皇后的列的位置,将列数赋给 a[k] public static void printQueenR(int k,int N,int []a){ if(N == k) { //如果当前行已经超过最大行了,说明某一种放置方法已经好了,输出 for(int i = 0;i < N;i++) { System.out.print(a[i] + 1 + " "); } System.out.print(" "); return ; } for(int i = 0; i < N;i++) { //第k行的第i列 a[k] = i; //先将k行的列数置为i if(legal(k,a)) //k行的列数为i时,是否和0--(k-1)行冲突了,没有则递归调用,继续寻找k+1行的位置 printQueenR(k+1,N,a); //就算进入递归,结束后也继续循环,i++,再尝试k行的另一个列位置,尝试找出另一种解 } } //(非递归) public static void printQueen(int a[],int N) { int t = 0; a[0] = -1; //初始条件,第0行的默认列置为-1 while(t >= 0) { //t代表行数 ,a[t]代表第t行第a[t]列 a[t]++; //每循环一次,则尝试本行的下一列 while(a[t] < N && !legal(t,a)) { //在本t行顺序找到一个不冲突的列的位置 a[t]++; } if(a[t] < N) { //如果这个列合法,即不超出棋盘 if (t == N - 1) { //且目前的t行已经到头了,则输出本次放置方案 for (int i = 0; i < N; i++) { System.out.print(a[i] + 1 + " "); } System.out.print(" "); } else //没到头就行数+1,准备开始下一行。由于下一行一个列都没试过,并把下一行的初始列置为-1 a[++t] = -1; } else { //如果列已经试到超出棋盘了,说明t行的所有列都试过了还没有找到合适的位置,则返回下一行找另一种方案。 t--; //当t<0,时执行这句话说明第0行的所有列都尝试完毕,直接退出循环
} } } //公共方法。剪枝条件:检查第k行的放置的列是否合法,即是否与0--(k-1)行的放置冲突 public static boolean legal(int k,int a[]) { for (int i = 0; i < k; i++) {
//与其他行的摆放成一列,或成斜线就不合法 if(a[k] == a[i] || abs(k - i) == abs(a[i]-a[k])) { return false; } } return true; } }
这里运用了一个剪枝条件大大减少了复杂度。若是蛮力破解的话复杂度是:O(N^N),因为每一行都要试N次,一共N行。剪枝后的复杂度挺难计算的,最坏是O(N!),一般是指数级的样子