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.."] ]
分析
这是一个回溯算法,和二叉树遍历有点像。用一个stack来装当前探查的路径,基本框架还是:
初始化栈和当前探查的指针,以及方向变量while(栈不空)(或者指针有效吧之类的判断){if(往前){if(有子节点可用)去子节点(指针后移或者当前节点进栈);else方向变量反向}else{if(有兄弟节点可用){去兄弟节点;方向变量反向;}else回退(出栈或者指针前移);}}
这里看到有两个重要判断,一个是判断是否有子节点可用,一个是判断是否有兄弟节点可用。这两部分判断有时候是有重复的部分。
拿到这道题上说,子节点可用,需要判断的是比如已经放了3个queen,再放一个的话,要看子节点是否满足第4个queen和前三个不冲突;
兄弟节点可用,比如第一个queen放在了位置0,第二个queen放在了位置2,此时回退到queen2,那么就要看如果将这个queen放在位置3时是否与第一个queen冲突。
可以看最后那个代码,就是两个判断都有,但是两段代码非常接近。
其实这两个判断是重复的。
可以将之合并,办法就是:
比如这道题的话对于子节点的判断要优先一些(先剔除没必要进行的判断),就可以把这个强判断放到子节点部分执行;而兄弟节点判断部分只是简单的看是否越界。
1 public class Solution { 2 ArrayList<String[]> result = null; 3 public ArrayList<String[]> solveNQueens(int n) { 4 // Note: The Solution object is instantiated only once and is reused by each test case. 5 result = new ArrayList<String[]>(); 6 if(n < 1) return result; 7 int[] s = new int[n]; 8 String[] out = new String[n]; 9 solveNQ(0, s, out); 10 return result; 11 } 12 //0 valid 13 //1 ocuppied 14 //2 cannot put, column 15 //3 cannot put, slop/left 16 //4 cannot put, slop/right 17 public void solveNQ(int r,int[] map, String[] s) 18 { 19 if(r == map.length) 20 { 21 result.add(s); 22 return; 23 } 24 for(int i = 0; i < map.length; i ++) 25 { 26 if(map[i] == 1) 27 { 28 if(i > 0 && map[i - 1] == 0) 29 { 30 map[i - 1] = 3; 31 } 32 if(i + 1 < map.length && map[i + 1] == 0) 33 { 34 map[i + 1] = 4; 35 } 36 map[i] = 2; 37 } 38 if(map[i] == 3) 39 { 40 if(i > 0 && map[i - 1] == 0) 41 { 42 map[i - 1] = 3; 43 } 44 map[i] = 0; 45 } 46 if(map[i] == 4) 47 { 48 if(i + 1 < map.length && map[i + 1] == 0) 49 { 50 map[i + 1] = 4; 51 } 52 map[i] = 0; 53 } 54 } 55 for(int i = 0; i < map.length; i ++) 56 { 57 if(map[i] == 0) 58 { 59 s[r] = generateRow(map.length, i + 1); 60 solveNQ(r + 1, map, s); 61 } 62 } 63 } 64 public String generateRow(int n, int k){ 65 assert(k>0 && k<=n); 66 StringBuilder res = new StringBuilder(""); 67 for(int i=0;i<k-1;i++){ 68 res.append("."); 69 } 70 res.append("Q"); 71 for(int i=k;i<n;i++){ 72 res.append("."); 73 } 74 return res.toString(); 75 } 76 }
没发现那里错了,但是跑不出来。
正解::
1 public class Solution { 2 public ArrayList<String[]> solveNQueens(int n) { 3 // Start typing your Java solution below 4 // DO NOT write main() function 5 assert(n>=0); 6 ArrayList<String[]> res = new ArrayList<String[]>(); 7 solveNQueues(1, new int[n], res); 8 return res; 9 } 10 11 public void solveNQueues(int k, int[] solu, ArrayList<String[]> res){ 12 int n= solu.length; 13 main: 14 for(int i=0;i<n;i++){ 15 for(int j=0;j<k-1;j++) 16 if(solu[j]==i || Math.abs(solu[j]-i)==Math.abs(j-k+1)) continue main; 17 solu[k-1]=i; 18 if(k==n){ 19 String[] temp = new String[n]; 20 for(int l=0;l<n;l++){ 21 temp[l]=generateRow(n,solu[l]+1); 22 } 23 res.add(temp); 24 }else 25 solveNQueues(k+1,solu,res); 26 } 27 } 28 29 public String generateRow(int n, int k){ 30 assert(k>0 && k<=n); 31 StringBuilder res = new StringBuilder(""); 32 for(int i=0;i<k-1;i++){ 33 res.append("."); 34 } 35 res.append("Q"); 36 for(int i=k;i<n;i++){ 37 res.append("."); 38 } 39 return res.toString(); 40 } 41 }
把棋盘存储为一个N维数组a[N],数组中第i个元素的值代表第i行的皇后位置,这样便可以把问题的空间规模压缩为一维O(N),在判断是否冲突时也很简单,首先每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了,其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可。至于斜线冲突,通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,即| row – i | = | col – a[i] | 。这样某个位置是否可以放置皇后的问题已经解决。
我的解法:
注意 查对角线的方法是:
对于两个queen (x1, y1) 和 (x2, y2), 如果 Math.abs(x1 - x2) == Math.abs(y1 - y2), 则在同一条对角线上。
1 public class Solution { 2 ArrayList<String[]> result = null; 3 public ArrayList<String[]> solveNQueens(int n) { 4 // Start typing your Java solution below 5 // DO NOT write main() function 6 result = new ArrayList<String[]>(); 7 solve(new int[n], 0); 8 return result; 9 } 10 public int solve(int[] arr, int pos){ 11 int size = arr.length; 12 if(pos == size) return generate(arr); 13 for(int i = 0; i < size; i ++) 14 if(isValid(arr, pos, i)){ 15 arr[pos] = i; 16 solve(arr, pos + 1); 17 } 18 return -1; 19 } 20 public boolean isValid(int[] arr, int pos, int check){ 21 for(int i = 0; i < pos; i ++) 22 if(check == arr[i] || Math.abs(pos - i) == Math.abs(check - arr[i])) return false; 23 return true; 24 } 25 public int generate(int[] arr){ 26 String[] exp = new String[arr.length]; 27 for(int i = 0; i < arr.length; i ++){ 28 StringBuffer sb = new StringBuffer(); 29 for(int j = 0; j < arr.length; j ++){ 30 if(j == arr[i]) sb.append("Q"); 31 else sb.append("."); 32 } 33 exp[i] = sb.toString(); 34 } 35 result.add(exp); 36 return 1; 37 } 38 }