算法02:N 皇后問題

描述

整數 N 個皇后放在 N * N 的棋盤上,要求每個皇后不能相互攻擊,即同一行、同一列、同一斜線上不能同時存在兩個皇后,有多少種不同的方法?

分析

棋盤是個二維數組,但是每一行只有一個元素,那麼可以用一維數組 board 表示棋盤, board[i] = j 表示第 i 個(行)皇后放在第 j 列上,此時再看條件

  • 不能處於同一行:由於一維數組下標表示皇后所在行,則不用考慮兩個皇后處在同一行,因爲數組下標不可能重複;
  • 不能處於同一列:對於第 i 個皇后來說,前面 i-1 個皇后均不能和第 i 個皇后所處的列相同,即 board[i] != board[0...(i - 1)];
  • 不能處於同一斜線:對於第 i 個皇后,假設 board[i] = a, 則對於前面 i-1 個皇后中的任意一個 board[j] = b, 必須滿足 |i - j| != |a - b|

代碼

採用遞歸方式,先校驗第 1 個皇后是否能擺放在第 1 列,如果可以則放置皇后並遞歸第 2 個皇后,否則檢驗第 1 個皇后是否能放置在第 2 列,如果可以則放置皇后並遞歸調用第 2 個皇后,否則繼續檢驗第 3...n 列,直到最後一個皇后放置後遞歸結束。

代碼如下

    /**
     * 返回 N 個皇后共有多少种放置方式
     * @param N
     * @return
     */
    public static int sum(int N) {
        if(N == 0) {
            return 0;
        }
        // 一維數組 board[i] = j 表示第 i 個皇后放置在第 j 列
        int[] board = new int[N];
        // 從第一個皇后開始遞歸
        return process(board, 0, N);
    }

    /**
     * 從第 row 個皇后開始擺放,返回 n 個皇后共有多少种放置方法
     * @param board
     * @param row
     * @param n
     * @return
     */
    private static int process(int[] board, int row, int n) {
        // 當最後一行放置皇后,則返回1表示完成一種擺放方式
        // 因爲是從0開始擺放,此時實際是滴n-1行擺放成功即可認爲完成了,下一次調用時正好加到了n
        if(row == n) {
            return 1;
        }
        int result = 0;
        for(int col=0; col<n; col++) {
            // 對於第 row 個皇后,需要檢驗是否與前面 row - 1 個皇后是否有衝突
            if(isValid(board, row, col)) {
                // 如果沒衝突,則講皇后擺放在該位置上
                board[row] = col;
                // 進行擺放第 row + 1 個皇后
                result += process(board, row+1, n);
            }
        }
        return result;
    }

    /**
     * 檢驗第 row 個皇后是否可以擺放在第 col 列上
     * @param board
     * @param row
     * @param col
     * @return
     */
    private static boolean isValid(int[] board, int row, int col) {
        for(int i=0; i<row; i++) {
            if(board[i] == col || Math.abs(row - i) == Math.abs(board[i] - col)) {
                return false;
            }
        }
        return true;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章