N皇后問題的遞歸與非遞歸解法


輸入一個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("\n");
            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("\n");
                }
                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!),一般是指數級的樣子


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM