回溯法经典问题之八皇后问题

回溯法经典问题之八皇后问题

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
攻击的范围为同一行,同一类或同一对角线。此处使用Leetcode题目:https://leetcode-cn.com/problems/n-queens-ii/
首先考虑暴力解法:即在第一行第一个位置上放置一个皇后,接着在第二行与第一行不在同一列位置放置一个皇后,…,根据数学推导,n皇后问题的时间复杂度为O(n!)。
因此需要寻求其他解法。若在第一行第一列放置一个皇后,第二列在不与第一个皇后位于同一列的位置放置一个皇后,以此类推,当发现下一列无法在任何位置放置一个皇后时,调整上一行皇后的位置,再次进行下去,直到调整第一行的皇后的位置。此解法正是回溯的思想,且满足深度优先搜索(DFS)。在DFS的过程中需要记录已经放置过皇后的位置,以便下一次放置时判断与之前放置的皇后是否发生攻击。注意到每一行只允许一个皇后存在,即行数与该行皇后所在的列数是一一对应的,因此可以使用数组结构作为记录皇后的位置的数据结构。即array[i]=j表示第i+1行皇后在第j+1列。
在进行DFS过程中,首先进行边界条件判断,边界条件就是当前皇后已经全部放置完毕;未放置到最后一行,需要遍历每一列,寻找到合适的列位置放置皇后,从而进入下一列;在判断是否是合法位置的时候,需要判断当前放置的皇后是否与历史放置的皇后处理同一列与同一对角线上。如何判断当前放置的皇后与历史皇后处于同一对角线呢?对角线都是45度,将棋盘格想象成坐标系,横轴的起始位置差与纵轴的起始位置差相等。
当前需要在第row行的第col列放置皇后,从第0行到row-1行开始遍历,第i行皇后的位置是pos=array[i];那么纵轴(行方向)的位置差为row-i,横轴(列方向)的位置差为col-pos,(i,pos)与(row,col)处于同一右对角线满足row-i == col-pos,处于同一左对角线满足row-i==pos-col.

import java.util.ArrayList;
import java.util.List;

/**
 * N皇后问题的解法,回溯算法,先让Q在第一行的第一列,将第二行的Q放置在合理的位置,接着放置第三列,以此类推直到没有合理的位置放置Q,则回溯到上一行调整Q的位置。
 */
public class N皇后问题 {
    public List> solveNQueens(int n) {
        List> res = new ArrayList<>();
        int[] queenList = new int[n];//int[j]=j表示第i+1行的第j+1列放置皇后,因为每一行只能有一个位置放置皇后,因此可以用数组表示
        placeQueen(queenList, 0, n, res);  //从第0行开始
        return res;
    }

    private void placeQueen(int[] queenList, int row, int n, List> res) {
        //先解决边界条件
        if (row == n) {  //最后一列已经放置完成,说明此解合理,将其加入结果集中
            List tmpList = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                StringBuilder str = new StringBuilder();
                for (int j = 0; j < n; j++) {
                    if (queenList[i] == j)
                        str.append("Q");
                    else
                        str.append(".");
                }
                tmpList.add(str.toString());
            }
            res.add(tmpList);
            return;
        }

        //未到达边界条件
        for (int col = 0; col < n; col++) {
            if (isValid(queenList, row, col)) {
                queenList[row] = col;
                placeQueen(queenList, row + 1, n, res);
            }
        }
    }

    private boolean isValid(int[] queenList, int row, int col) {
        for (int i = 0; i < row; i++) {
            int pos = queenList[i];
            if (pos == col)  //跟已经有的在同一列
                return false;
            if (col - pos == row - i)  //跟已经有的在同一右对角线
                return false;
            if (pos - col == row - i)  //跟已经有的在同一左对角线
                return false;
        }
        return true;
    }

    public static void main(String[] args) {
        N皇后问题 queen = new N皇后问题();
        List> lists = queen.solveNQueens(8);
        System.out.println(lists);
        System.out.println("解法的种类为:" + lists.size());
    }
}

你可能感兴趣的:(N皇后问题)