[工作準備--演算法] 八皇后問題--遞迴求解
阿新 • • 發佈:2018-11-01
目標
在8*8的的國際象棋中擺上八個皇后 使其不能相互攻擊
問題分析
問題的解向量:(x0,x2,x3,….,x7)
- 採用陣列的下標i 表示皇后的所在行
- 採用陣列元素x[i]表示皇后的所在列向量
約束條件
- 顯示約束 (對解向量的直接約束) xi=1,2,3…n
- 隱示約束1 :任意兩個皇后不同列 :
- 隱示約束2 :兩個皇后不處於同一對角線 :
/**
*遞迴呼叫函式 放置第t 行的皇后
* @param t
*/
public void Backtrack(int t) {
//遞迴終止條件 即第九行的皇后即結束(八皇后問題 注意陣列的下標從0開始)
if (t>7) {
//輸出放置皇后情況並且count++
output(x);
}
else {
//迴圈是遍歷在第一行上放置皇后
for(int i=0;i<8;i++) {
x[t] = i;
//如果符合約束條件 就放置下一行的皇后
if(Bound(t)) {
Backtrack(t+1);
}
}
}
}
/**
* 判斷 k行第 i列放置皇后 是否在約束條件中
* @param k 第幾行要放置皇后
* @return
*/
public boolean Bound(int k ) {
for(int i =0;i<k ;i++) {
if((Math.abs(k-i)==Math.abs(x[k]-x[i]))||x[i]==x[k]) {
return false;
}
}
return true;
}
最後的整體呼叫和類的實現程式碼如下(完整程式碼)
public class Queen {
private int count =0;
private int[] x = new int [8];
public static void main(String [] args) {
Queen q = new Queen();
q.Backtrack(0);
System.out.println(q.count);
}
/**
* 判斷當前放置的 k = i 是否在約束條件中
* @param k 第幾行要放置皇后
* @return
*/
public boolean Bound(int k ) {
for(int i =0;i<k ;i++) {
if((Math.abs(k-i)==Math.abs(x[k]-x[i]))||x[i]==x[k]) {
return false;
}
}
return true;
}
/**
*遞迴呼叫函式 放置第t 行的皇后
* @param t
*/
public void Backtrack(int t) {
//遞迴終止條件 即第九行的皇后即結束(八皇后問題 注意陣列的下標從0開始)
if (t>7) {
//輸出放置皇后情況並且count++
output(x);
}
else {
//迴圈是遍歷在第一行上放置皇后
for(int i=0;i<8;i++) {
x[t] = i;
//如果符合約束條件 就放置下一行的皇后
if(Bound(t)) {
Backtrack(t+1);
}
}
}
}
private void output(int []x) {
count++;
for(int x1 :x) {
System.out.print(x1+" ");
}
System.out.println();
}
}
思路總結
- 這裡使用了遞迴呼叫使得設計思想的時候更加簡便和易讀易懂了.但是遞迴呼叫在效率上因為要壓棧(jvm棧–>執行緒安全的)等一系列的操作,使得效率不高效,同時所有的遞迴呼叫都可以用迴圈完成,只是不容易思考的設計而已.
- 在使用遞迴呼叫時候一定要注意到遞迴結束標誌,如果沒有遞迴結束標誌就會一直遞迴下去
- 當遇到大量的資料時候最好不要使用遞迴呼叫,因為遞迴呼叫佔用jvm棧的空間,而棧的空間是有限的.需要合理的使用.即迴圈遞迴巢狀層數不宜太多.