• 算法题练习——用Java、Python题解NC39 N皇后问题


    描述

    皇后问题是指在 n * n 的棋盘上要摆 n 个皇后。

    要求:

    任何两个皇后不同行,不同列也不在同一条斜线上,

    求给一个整数 n ,返回 n 皇后的摆法数。

    数据范围: 1≤n≤9

    要求:

    空间复杂度 O(1) ,时间复杂度 O(n!)

    例如当输入4时,对应的返回值为2,

    对应的两种四皇后摆位如下图所示:

    示例1

    输入:1

    返回值:1

    示例2

    输入:8

    返回值:92

    思路:

            从题中给出的有效信息:

    • 任何两个皇后不同行,不同列也不再同一条斜线上,计算n皇后的排列种类

            由于此题需要对nxn棋盘中的每个点进行判断,在符合情况的点再进行选择,穷举棋盘是不可避免,故此我们可以使用回溯的方法进行解决此问题

    方法一:常用-集合

    1. 设置三个集合分别记录不能再被选中的的列col,正斜线pos,反斜线neg
    2. 经规律发现 行号i - 列号j 可确定唯一正斜线,行号i + 列号j 可确定唯一反斜线
    3. 符合要求的点记录当前点并递归下一个皇后,最后一个皇后成功安置后将res+1,然后需回溯回初始点将初始点删除,将初始点的皇后放置其他位置进行判断
    4. 不符合要求的需要进行循环

    Java代码解:

    1. import java.util.*;
    2. public class Solution {
    3. /**
    4. *
    5. * @param n int整型 the n
    6. * @return int整型
    7. */
    8. Set column = new HashSet(); //标记列不可用
    9. Set posSlant = new HashSet();//标记正斜线不可用
    10. Set conSlant = new HashSet();//标记反斜线不可用
    11. int result = 0;
    12. public int Nqueen (int n) {
    13. // write code here
    14. compute(0, n);
    15. return result;
    16. }
    17. private void compute(int i, int n){
    18. if(i == n){
    19. result++;
    20. return;
    21. }
    22. for(int j = 0; j < n; j++){
    23. if(column.contains(j) || posSlant.contains(i - j) || conSlant.contains(i + j)){
    24. continue;
    25. }
    26. column.add(j);//列号j
    27. posSlant.add(i - j);//行号i - 列号j 正斜线
    28. conSlant.add(i + j);//行号i + 列号j 反斜线
    29. compute(i + 1, n); //计算下一行
    30. column.remove(j); //完成上一步递归计算后,清除
    31. posSlant.remove(i - j);
    32. conSlant.remove(i + j);
    33. }
    34. }
    35. }

    方法二:递归(推荐使用)

    知识点:递归与回溯

            递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

            如果是线型递归,子问题直接回到父问题不需要回溯,但是如果是树型递归,父问题有很多分支,我需要从子问题回到父问题,进入另一个子问题。因此回溯是指在递归过程中,从某一分支的子问题回到父问题进入父问题的另一子问题分支,因为有时候进入第一个子问题的时候修改过一些变量,因此回溯的时候会要求改回父问题时的样子才能进入第二子问题分支。

            n个皇后,不同行不同列,那么肯定棋盘每行都会有一个皇后,每列都会有一个皇后。如果我们确定了第一个皇后的行号与列号,则相当于接下来在n−1n-1n−1行中查找n−1n-1n−1个皇后,这就是一个子问题,因此使用递归:

    • 终止条件: 当最后一行都被选择了位置,说明n个皇后位置齐了,增加一种方案数返回。
    • 返回值: 每一级要将选中的位置及方案数返回。
    • 本级任务: 每一级其实就是在该行选择一列作为该行皇后的位置,遍历所有的列选择一个符合条件的位置加入数组,然后进入下一级。

            具体做法:

    • step 1:对于第一行,皇后可能出现在该行的任意一列,我们用一个数组pos记录皇后出现的位置,下标为行号,元素值为列号。
    • step 2:如果皇后出现在第一列,那么第一行的皇后位置就确定了,接下来递归地在剩余的n−1n-1n−1行中找n−1n-1n−1个皇后的位置。
    • step 3:每个子问题检查是否符合条件,我们可以对比所有已经记录的行,对其记录的列号查看与当前行列号的关系:即是否同行、同列或是同一对角线。

    python递归代码:

    1. class Solution:
    2. #判断皇后是否符合条件
    3. def isValid(self, pos: List[int], row:int, col:int):
    4. #遍历所有已经记录的行
    5. for i in range(row):
    6. #不能同行同列同一斜线
    7. if row == i or col == pos[i] or abs(row - i) == abs(col - pos[i]):
    8. return False
    9. return True
    10. #递归查找皇后种类
    11. def recursion(self, n:int, row:int, pos:List[int], res:int):
    12. #完成全部行都选择了位置
    13. if row == n:
    14. res += 1
    15. return int(res)
    16. #遍历所有列
    17. for i in range(n):
    18. #检查该位置是否符合条件
    19. if self.isValid(pos, row, i):
    20. #加入位置
    21. pos[row] = i
    22. #递归继续查找
    23. res = self.recursion(n, row + 1, pos, res)
    24. return res
    25. def Nqueen(self , n: int) -> int:
    26. res = 0
    27. #下标为行号,元素为列号,记录皇后位置
    28. pos = [0] * n
    29. #递归
    30. result = self.recursion(n, 0, pos, res)
    31. return result

  • 相关阅读:
    Hexagon_V65_Programmers_Reference_Manual(26)
    [极客大挑战 2019]Http1
    【SVM分类】基于matlab哈里斯鹰算法优化支持向量机SVM分类【含Matlab源码 2243期】
    极光推送Service
    宝塔+LNMP平台=HTTP文件共享服务
    WPF 稳定的全屏化窗口方法
    路由器漏洞的分类
    Git基础知识及基本操作
    浅看SpringBoot的自动装配
    YOLOv8 来了,快速上手实操
  • 原文地址:https://blog.csdn.net/weixin_53919192/article/details/126897998