回溯法解决N皇后问题(java)

**

回溯法:

**
简单理解:回溯法可以理解为通过选择不同的岔路口寻找目的地,一个岔路口一个岔路口的去尝试找到目的地。如果走错了路,继续返回来找到岔路口的另一条路,直到找到目的地。
.首先理解常见的三个名词:

1、路径:也就是已经做出的选择。

2、选择列表:也就是你当前可以做的选择。

3、结束条件:也就是到达决策树底层,无法再做选择的条件

核心框架如下:

result = []
void backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

框架理解:首先是将一个大问题转换为多个子问题
1.如果满足结束条件,则将当前的路径添加到返回结果的集合中;
2.若不满足结束条件,则要做选择,即遍历选择列表;
3.作出满足题目条件的选择,然后跳转到下一个问题,若该选择导致后面的选择无法完成,则得有撤销操作

利用回溯法,来分析N皇后的解法:
力扣51)N皇后的题目描述:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
皇后之间不能相互攻击的条件:
1.不能在同一行或者同一列上
2.在左右对角的位置不能放置皇后。

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

到此利用框架来写该问题的伪代码:

//首先,需要一个存储所有结果的集合result
List<List<String>> list = new ArrayList<>();
//这里的路径,其实是指整个棋盘,而此时,利用一个二维数组表示该棋盘,用0和1表示改位置是否可以选择
路径: int[][] path = new int[n][n];
//此时的选择列表指的是棋盘中未选择的位置。在该问题中,我们可以一行一行来做出选择,所以通过行数来确定结束条件
void backtrack(路径(path), 选择列表(int row)):
    if 满足结束条件(row == n):
        result.add(路径path)
        //注意:由于最后返回的是一个集合,且每一行都有.'和'Q'来代替
       //所以在这里需要将改二维数组的结果转换为标准输出形式,所以就有了后面代码中的Build()方法
        return
    //对于每一行来说,皇后到底放到哪一个位置都是选择,则当前的选择列表就是该行中的每一个位置
    //所以遍历该选择列表,就是遍历当前行的所有列(for(int i=0; i
    for 选择 in 选择列表:
    	//如果当前位置可以选择(此时有需要一个判断条件的方法isUsable())
    	if(isUsable()) {
     
    		做选择 //q其实就是让棋盘中对应的位置为1 path[row][col] = 1;
        	backtrack(path,row+1)
       		 撤销选择(若当前的选择导致后面的选择无法完成,则需要撤销该操作)
    	}
        

Ok,到这里分析的就差不多了:
1.需要一个方法build()来将棋盘的格式改为题目中要求的格式
2.需要一个方法isUsable() 来判断当前位置是否可用
3.套用回溯框架,写出代码,如下:

class Solution {
     
    List<List<String>> result = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
     
        int[][] path = new int[n][n];
        dfs(path,0,n);
        return result;
    }
    public void dfs(int[][] path,int row, int n) {
     
        if(row == n) {
     
            result.add(build(path,n));
            return;
        }
        for(int col=0; col<n; col++) {
     
            //如果当前位置可用,才进行选择,回溯,撤回
            if(isUsable(path,row,col,n)) {
     
                path[row][col] = 1;
                dfs(path, row+1, n);
                path[row][col] = 0;
            }
        }
    }
    
    //需要将最后的数组转换为List形式
    public List<String> build(int[][] path, int n) {
     
        List<String> list = new ArrayList<>();
        for(int i=0; i<n; i++) {
     
            StringBuilder sb = new StringBuilder();
            for(int j=0; j<n; j++) {
     
                if(path[i][j] == 0) {
     
                    sb.append('.');
                }else {
     
                    sb.append('Q');
                }
            }
            list.add(sb.toString());
        }
        return list;
    }
    //判断当前位置是否可用(path[row][col]):
    //1.当前列上不得有=1的
    //2.左对角的位置不能有1
    //3.右对角的位置不得为空
    public boolean isUsable(int[][] path, int row,int col,int n) {
     
        //1.当前列上不得有=1的
        for(int i=row-1; i>=0; i--) {
     
            if(path[i][col] == 1) {
     
                return false;
            }
        }
        //2.左对角的位置不能有1
        for(int i=col-1; i>=0; i--) {
     
            if(row+i-col<0) {
     
                break;
            }
            if(path[row+i-col][i] == 1) {
     
                return false;
            }
        }
        //3.右对角的位置不得为空
        for(int i = col+1; i<n; i++) {
     
            if(row+col-i<0) {
     
                break;
            }
            if(path[row+col-i][i] == 1) {
     
                return false;
            }
        }
        return true;

    }
}

以上文章 属于自己的初步理解,如果觉得有点绕,下面链接中利用全排列讲解该方法比较清晰,可参考:
链接:https://leetcode-cn.com/problems/n-queens/solution/hui-su-suan-fa-xiang-jie-by-labuladong/
来源:力扣(LeetCode)

你可能感兴趣的:(力扣刷题算法,算法)