算法分析-回溯算法-求解N皇后问题

一.题目需求

n皇后问题是一道比较经典的算法题。它研究的是将n个皇后放置在一个n×n的棋盘上,使皇后彼此之间不相互攻击。

即任意两个皇后都不能处于同一行、同一列或同一斜线上。

二.算法思想

1.构建棋盘

可以用一个n×n列表来表示棋盘,设皇后所在的位置为board[i],i代表行,board[i]代表列,因此皇后所处的位置就是第i行、第board [i]列。

如下,第一个皇后就处于[0,0]位置(以0为起点,[0,0]意为第一行第一列),第二个皇后就处于[2,3]位置(意为第三行第四列):

2.不攻击检查

即需要判断:
1)是否处于同一列中
2)是否在左斜线上:(行 + 列)的值不可相等
3)是否在右斜线上:(列 - 行)的值不可相等
这里,每行肯定只有1个皇后,是很显然的,因此不必特别判断,
左右斜线的判断可以用一个绝对值公式abs(board[i] - col) == abs(i - row)判断,这样就不需要写两个公式。

    # 校验是否有效def is_valid(board, row, col):for i in range(row):if board[i] == col or abs(board[i] - col) == abs(i - row):return Falsereturn True

3.DFS搜索,回溯算法
1)结束条件:当前行数 = 皇后总数,即最后一行已经成功放入皇后
2)循环一行中的每一个位置,若不发生攻击事件,可将皇后放入该位置
3)继续下一行的搜索,即传入的参数为当前行数 + 1

    # DFS搜索,回溯算法def backtrack(board, row):# 探索行号等于n时结束if row == n:result.append(board[:])return# 根据当前行号,再遍历每一列位置for col in range(n):# 检测当前行号,列号是否有效if is_valid(board, row, col):# 有效则设置该位置为皇后board[row] = col# 探索下一行,每次探索一行,放置1个皇后backtrack(board, row + 1)

4.算法分析

这个算法的时间复杂度是O(n!),因为总共有n!种可能的摆放方式。空间复杂度:O(n),用于存储递归调用栈。

.编程实现

根据网上搜集学到的实现代码,多数都采用一维数组方式实现,每次探索每行的每一列,代码更简洁。

实现方法一:

class SolutionNQueens(object):'''回溯算法-一维数组解决N皇后问题。该算法的时间复杂度为:O(n!),因为总共有n!种可能的摆放方式。空间复杂度:O(n),用于存储递归调用栈。'''def __init__(self, num):self.count = 0self.num = num# 校验当前行号,列号是否有效def is_valid(self, board, row, col):# 遍历行号for i in range(row):if board[i] == col or abs(board[i] - col) == abs(i - row):return Falsereturn Truedef backtrack(self, board, row, result):if row == self.num:result.append(board[:])self.count += 1returnfor col in range(self.num):if self.is_valid(board, row, col):board[row] = colself.backtrack(board, row + 1, result)board[row] = 0def backtrack_result(self):result = []# 最终皇后的位置 (下标:第几行 数值:第几列)board = [0] * self.num# 从第一行开始row = 0self.backtrack(board, row, result)return result

同样采用一维数组方式实现,优化减少部分无效列号的遍历,每次探索部分列即可,耗时减少很多。

实现方法二:

class SolutionNQueensNew(object):'''回溯算法-一维数组解决N皇后问题,优化减少部分无效列号的遍历。该算法的时间复杂度为:O(n!),因为总共有n!种可能的摆放方式。空间复杂度:O(n),用于存储递归调用栈。'''def __init__(self, num):self.count = 0self.num = num# 校验当前行号,列号是否有效def is_valid(self, board, row, col):# 遍历行号for i in range(row):if board[i] == col or abs(board[i] - col) == abs(i - row):return Falsereturn Truedef backtrack(self, board, row, range_col, result):if row == self.num:result.append(board[:])self.count += 1return# 根据当前行号,再遍历列号表中的列号for col in range_col:if self.is_valid(board, row, col):# 有效则设置该位为 皇后board[row] = col# 列号表中删除该皇后位的列号,减少无效遍历次数range_col.remove(col)# 探索下一行,每次探索一行,放置1个皇后self.backtrack(board, row + 1, range_col, result)# 探索失败,回溯,还原该位置为 0-空位board[row] = 0# 还原列号表,列表尾部添加元素range_col.append(col)# sort 增序排序range_col.sort()def backtrack_result(self):result = []# 最终皇后的位置 (下标:第几行 数值:第几列)board = [0] * self.num# 从第一行开始row = 0# 列号表初始化,每一列都探索range_col = [i for i in range(self.num)]self.backtrack(board, row, range_col, result)return result

采用二维数组方式实现,每次探索每行每列,代码稍微复杂点,检测是否有效方法也不同。

实现方法三:

def solve_n_queens(n):'''回溯算法-二维数组解决N皇后问题该算法的时间复杂度为:O(n!),因为总共有n!种可能的摆放方式。空间复杂度:O(n),用于存储递归调用栈。'''def is_valid(board, row, col):'''board(一个二维列表,表示棋盘),row(一个整数,表示要检查的行索引),col(一个整数,表示要检查的列索引)。函数的目的是检查在给定的行和列上放置一个皇后是否有效。''''''函数首先遍历当前行之前的所有行,检查是否有任何皇后在同一列上。如果有,函数返回False,表示放置皇后无效。'''for i in range(row):if board[i][col] == 1:return False'''zip循环检查左上对角线上的单元格。如果在这些单元格中找到一个皇后,函数同样返回False。'''for i, j in zip(range(row - 1, -1, -1), range(col - 1, -1, -1)):if board[i][j] == 1:return False'''zip循环检查右上对角线上的单元格。如果在这些单元格中找到一个皇后,函数同样返回False。'''for i, j in zip(range(row - 1, -1, -1), range(col + 1, n)):if board[i][j] == 1:return Falsereturn Truedef backtrack(board, row):# 探索行号等于N时结束if row == n:# 将棋盘可行方案数据添加到结果列表中result.append([[board[i][j] for j in range(n)] for i in range(n)])return# 根据当前行号,再遍历列号for col in range(n):# 检测当前行号,列号是否有效if is_valid(board, row, col):# 有效则设置该方格为 1-皇后board[row][col] = 1# 探索下一行,每次探索一行,放置1个皇后backtrack(board, row + 1)# 探索失败,回溯,还原该方格为 0-空位board[row][col] = 0# 返回结果列表result = []# 创建n×n的棋盘,2维数组,其中1表示皇后,0表示空格board = [[0] * n for _ in range(n)]# 回溯算法,从第1行开始探索backtrack(board, 0)return result

采用二维数组方式实现,优化减少部分无效列号的遍历,每次探索部分列即可,耗时减少很多。

实现方法四:

def solve_n_queens_new(n):'''回溯算法-二维数组解决N皇后问题,优化减少部分无效列号的遍历。该算法的时间复杂度为:O(n!),因为总共有n!种可能的摆放方式。空间复杂度:O(n),用于存储递归调用栈。'''def is_valid(board, row, col):'''board(一个二维列表,表示棋盘),row(一个整数,表示要检查的行索引),col(一个整数,表示要检查的列索引)。函数的目的是检查在给定的行和列上放置一个皇后是否有效。''''''zip循环检查左上对角线上的单元格。如果在这些单元格中找到一个皇后,函数同样返回False。'''for i, j in zip(range(row - 1, -1, -1), range(col - 1, -1, -1)):if board[i][j] == 1:return False'''zip循环检查右上对角线上的单元格。如果在这些单元格中找到一个皇后,函数同样返回False。'''for i, j in zip(range(row - 1, -1, -1), range(col + 1, n)):if board[i][j] == 1:return False'''函数首先遍历当前行之前的所有行,检查是否有任何皇后在同一列上。如果有,函数返回False,表示放置皇后无效。'''for i in range(row):if board[i][col] == 1:return Falsereturn Truedef backtrack(board, row, range_col):# 探索行号等于N时结束if row == n:# 将棋盘可行方案数据添加到结果列表中result.append([[board[i][j] for j in range(n)] for i in range(n)])return# 根据当前行号,再遍历列号表中的列号for col in range_col:# 检测当前行号,列号是否有效if is_valid(board, row, col):# 有效则设置该方格为 1-皇后board[row][col] = 1# 列号表中删除该皇后位的列号,减少无效遍历次数range_col.remove(col)# 探索下一行,每次探索一行,放置1个皇后backtrack(board, row + 1, range_col)# 探索失败,回溯,还原该方格为 0-空位board[row][col] = 0# 还原列号表,列表尾部添加元素range_col.append(col)# sort 增序排序range_col.sort()# 返回结果列表result = []# 创建n×n的棋盘,2维数组,其中1表示皇后,0表示空格board = [[0] * n for _ in range(n)]# 列号表初始化,每一列都探索range_col = [i for i in range(n)]# 回溯算法,从第1行开始探索backtrack(board, 0, range_col)return result

.运行结果

1,4种方法测试对比下耗时。

经过部分优化,减少已排放皇后位对应列号探测,明显可以减少整体耗时。

if __name__ == '__main__':nums = 10all_dis_time = 0.0# 循环10次,求平均值for i in range(nums):start_time = time.time()################################ num: 皇后的数量n = 10'''回溯算法-一维数组解决N皇后问题皇后的数量 = 10可行方案数: 724平均时间:180.8545毫秒'''# s = SolutionNQueens(n)'''回溯算法-一维数组解决N皇后问题,优化减少部分无效列号的遍历.皇后的数量 = 10可行方案数: 724平均时间:78.5564毫秒'''s = SolutionNQueensNew(n)# 参数:皇后总数  位置结果  当前放置第几行solutions = s.backtrack_result()print('可行方案数:', s.count)# 打印皇后在棋盘位置# for solution in solutions:#     print('======================')#     for row in solution:#         print(" ▢ " * row + " Q " + " ▢ " * (n - row - 1))# print('======================')'''回溯算法-二维数组解决N皇后问题皇后的数量 = 10可行方案数: 724平均时间:199.6063毫秒'''# grid_board = solve_n_queens(n)'''回溯算法-二维数组解决N皇后问题,优化减少部分无效列号的遍历.皇后的数量 = 10可行方案数: 724平均时间:117.3587毫秒'''# grid_board = solve_n_queens_new(n)# rst_nums = len(grid_board)# print("可行方案数:", rst_nums)# for i in range(rst_nums):#     print("方案:", (i + 1))#     # 打印网格地图#     grid_print(grid_board[i])################################ 识别时间end_time = time.time()# 计算耗时差,单位毫秒dis_time = (end_time - start_time) * 1000# 保留2位小数dis_time = round(dis_time, 4)all_dis_time += dis_timeprint('时间:' + str(dis_time) + '毫秒')print('=============================')pre_dis_time = all_dis_time / nums# 保留4位小数pre_dis_time = round(pre_dis_time, 4)print('平均时间:' + str(pre_dis_time) + '毫秒')

2,动态演示求解4皇后问题完整过程。

 =====================end =====================

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2660735.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Vue 问题解决

一、问题:TypeError: (0 , _message.default) is not a function 当没有default时,在其他页面import引入的时,必须加{}。 二、问题:Vue前端页面的表格数据总是一行一行的显示 使用Async/Await来解决前端数据一行一行显示的问题。可以将获取部…

vue+element+springboot实现多张图片上传

1.需求说明 2.实现思路 3.el-upload组件主要属性说明 4.前端传递MultipartFile数组与服务端接收说明 5.完整代码 1.需求说明 动态模块新增添加动态功能,支持多张图片上传.实现过程中对el-upload组件不是很熟悉,踩了很多坑,当然也参考过别的文章,发现处理很…

Spark RDD操作性能优化技巧

Apache Spark是一个强大的分布式计算框架,用于处理大规模数据。然而,在处理大数据集时,性能优化成为一个关键问题。本文将介绍一些Spark RDD操作的性能优化技巧,帮助大家充分利用Spark的潜力,并获得更快的处理速度。 …

2023.12.27 关于 Redis 数据类型 List 常用命令

目录 List 类型基本概念 List 类型特点 List 操作命令 LPUSH LPUSHX RPUSH RPUSHX LRANGE LPOP RPOP LINDEX LINSERT LREM LTRIM LSET 阻塞版本的命令 阻塞版本 和 非阻塞版本的区别 BLPOP & BRPOP List 类型基本概念 Redis 中的列表(list&am…

第十一章 Stream消息驱动

Stream消息驱动 gitee:springcloud_study: springcloud:服务集群、注册中心、配置中心(热更新)、服务网关(校验、路由、负载均衡)、分布式缓存、分布式搜索、消息队列(异步通信)、数据库集群、…

Apache Commons JCS缓存解决方案

第1章:引言 大家好,我是小黑!今天,咱们来聊聊Apache Commons JCS,一个Java界里的缓存大杀器。缓存技术,对于提高应用性能来说,就像是给它加了一剂兴奋剂,能让数据访问变得快如闪电。…

Qt(二):使用udp发送与接收图片

使用Qt来通过UDP协议发送和接收图片可以分为几个步骤。以下是一个基本的指南: 发送图片准备图片数据:首先,你需要将图片转换为可以在网络上传输的数据格式。通常,这涉及到将图片转换为字节数组。设置UDP套接字:在Qt中…

OpenCV-Python(21):OPenCV查找及绘制轮廓

1.认识轮廓 1.1 目标 理解什么是轮廓学习掌握找轮廓、绘制轮廓等学习使用cv2.findContours()、cv2.drawContours()函数的用法 1.2 什么是轮廓 在OpenCV中,轮廓是图像中连续的边界线的曲线,具有相同的颜色或者灰度,用于表示物体的形状。轮廓…

linux用户态与内核态通过字符设备交互

linux用户态与内核态通过字符设备交互 简述 Linux设备分为三类,字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取,常见外设基本都是字符设备。块设备一般用于存储设备,一块一块的读取。网络设备,Linux将对网络…

web自动化(4)——POM设计重构

1. 什么是POM Page Object Model 是ui自动化测试中常见的封装方式。 原理:将页面封装为PO对象,然后通过面向对象的方式实现UI自动化 2. 封装原则 PO无需包含全部UI元素PO应当验证元素PO不应该包含断言PO不应该暴露元素 3. 怎么进行POM封装 面向对象…

Centos7:Jenkins+gitlab+node项目启动(2)

Centos7:Jenkinsgitlabnode项目启动(1) Centos7:Jenkinsgitlabnode项目启动(1)-CSDN博客 Centos7:Jenkinsgitlabnode项目启动(2) Centos7:Jenkinsgitlabnode项目启动(2)-CSDN博客 Centos7:Jenkinsgitlabnode项目启…

自动化网络故障修复管理

什么是故障管理 故障管理是网络管理的组成部分,涉及检测、隔离和解决问题。如果实施得当,网络故障管理可以使连接、应用程序和服务保持在最佳水平,提供容错能力并最大限度地减少停机时间。专门为此目的设计的平台或工具称为故障管理系统。 …

目标检测损失函数:IoU、GIoU、DIoU、CIoU、EIoU、alpha IoU、SIoU、WIoU原理及Pytorch实现

前言 损失函数是用来评价模型的预测值和真实值一致程度,损失函数越小,通常模型的性能越好。不同的模型用的损失函数一般也不一样。损失函数主要是用在模型的训练阶段,如果我们想让预测值无限接近于真实值,就需要将损失值降到最低…

vue3(十)-基础入门之Swiper轮播与自定义指令

一、Swiper html : 注意&#xff1a; class“swiper-wrapper”、class“swiper-slide” 等类名不能写错 <body><!-- 导入下载好的包或通过 CDN 导入vue、swiper.js、swiper.css --><!-- <script src"https://unpkg.com/vue3/dist/vue.global.js"&…

RK3568平台开发系列讲解(Linux系统篇)PWM系统编程

🚀返回专栏总目录 文章目录 一、什么是PWM二、PWM相关节点三、PWM应用编程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 PWM 的系统编程。 一、什么是PWM PWM,即脉冲宽度调制(Pulse Width Modulation)

【PowerMockito:编写单元测试过程中采用when打桩失效的问题】

问题描述 正如上图所示&#xff0c;采用when打桩了&#xff0c;但是&#xff0c;实际执行的时候还是返回null。 解决方案 打桩时直接用any() 但是这样可能出现一个mybatisplus的异常&#xff0c;所以在测试类中需要加入以下代码片段&#xff1a; Beforepublic void setUp() …

Awesome Chrome Form UI - 框架设计与基础实现

Money is not evil by itself. Its just paper with perceived value to obtain other things we value in other ways. If not money what is evil you may ask? Evil is the unquenchable, obsessive and moral bending desire for more. Evil is the bottomless,soulless …

解决VSCode中C/C++ Project Generator插件创建的项目只能运行单个程序的问题

初六&#xff0c;履霜&#xff0c;坚冰至。 释意&#xff1a;初六&#xff0c;当你踩着微霜之时&#xff0c;严寒与坚冰也就即将到来。 目录 一、前言 二、问题描述 三、解决方案 1、思路总结 2、思考过程 3、解决方案&#xff08;直接用&#xff0c;报错找我(&#xff8…

超声功率放大器怎么用

超声功率放大器是一种用于放大超声信号的设备&#xff0c;广泛应用于医疗领域、工业领域和科学研究中。它能够将超声信号的能量增加到足够大的水平&#xff0c;以便进行高强度超声疗法、材料加工和实验研究等应用。下面将详细介绍超声功率放大器的使用方法和其工作原理。 首先&…

数据结构——红黑树 and B-树

红黑树 根据平衡条件第4、5两点 最短路径&#xff0c;都是黑色 最长路径&#xff0c;红黑相间 最长是最短的两倍 B-树