python跳舞的线_舞蹈链(Dance Link X)算法详解及python实现
这两天打算做个数独玩玩,查了一下解数独最好的算法叫舞蹈链:Dance Link X
该算法主要是解决精确覆盖问题:比如有个集合X,以及其若干子集的集合Y,要求出一个Y的子集Y*,能够恰好分割X。
举个例子,有集合X = [1,2,3,4,5,6,7],以及其子集的集合Y:
集合1:[3,5,6]
集合2:[1,4,7]
集合3:[2,3,6]
集合4:[1,4]
集合5:[2,7]
集合6:[4,5,6]
要从Y的集合中选则若干集合,能够刚好覆盖X,每个元素出现且只出现一次。由上面的例子可知集合1,4,5所包含的元素能满足要求
舞蹈链算法也就是用来解决上述问题的,不过说这个是算法,倒是更像一种数据结构,因为首先解决问题之前需要构建一个链表,如下所示:
该链表的名称叫做:双向 十字 交叉 循环 链表,看起来就是很高端很霸气的名字,就像龙母的称谓一样:丹妮莉丝 坦格利安,旧瓦雷利亚的后裔,安达尔人先民的女王,维斯特洛的统治者暨全境守护者,大草原多斯拉克人卡丽熙,不焚者,弥林的女王,镣拷打破者,龙之母,豌豆射手,阿斯塔波的解放者,罗伊拿人和先民的女王,龙石岛公主
扯远了,简单解释一下,双向不用说,十字交叉一看就明白,这个链表的每一行每一列都是收尾相连的(Head不算),也就是循环的。
该链表的每个节点有六个属性,分别指向上下左右以及列节点,还有行号。
class CrossCycleLinkNode(object):
def __init__(self, x, row):
self.val = x
self.row = row
self.col = self
self.up = self
self.down = self
self.left = self
self.right = self
现在介绍一下算法的主要步骤,以及实现的函数:
def dance_link_x(head: CrossCycleLinkNode, answers: list):
head是头结点,answers是装答案(行号)的列表
从head的右边第一个节点(first_col)入手,将这一列的所有节点从链表中移除,并将恢复的操作压入栈中:
restores = []
first_col = head.right
first_col.removeColumn()
restores.append(first_col.restoreColumn)
上面的removeColumn和restoreColumn是一对作用相反的CrossCycleLinkNode成员方法,用来移除(或恢复)该节点所在的列上面的所有节点(移除或恢复行也有):
class CrossCycleLinkNode(object):
def removeColumn(self):
node = self
while True:
node.left.right = node.right
node.right.left = node.left
node = node.down
if node == self: break
pass
def restoreColumn(self):
node = self
while True:
node.left.right = node
node.right.left = node
node = node.down
if node == self: break
pass
def removeRow(self):
node = self
while True:
node.up.down = node.down
node.down.up = node.up
node = node.right
if node == self: break
pass
def restoreRow(self):
node =self
while True:
node.up.down = node
node.down.up = node
node = node.right
if node == self: break
pass
注意看代码,这仅仅改变了周边结点的指向,结点自身的指向还是没变的
如上图所示,first_col所在的列从链表中移除后,该列的节点仍然指向原来的地方,所以restores弹出恢复方法的时候调用就可以恢复到移除之前的样子
再往下走,遍历first_col上所有结点,将结点所在的行也从链表中移除(列节点除外),并且将恢复操作入栈
node = first_col.down
while node != first_col:
if node.right != node:
node.right.removeRow()
restores.append(node.right.restoreRow)
node = node.down
如果该节点所在行只有它自己则跳过,而且要从该节点的右(或左)节点调用移除方法,不能直接从该节点上调用,不然会死循环
上图是把节点4所在行移除后的样子
将节点10所在行移除后如上图所示
可以确定答案很定在4节点或10节点所在行的其中之一(如果答案存在)
所以轮流对4节点及10节点的行进行遍历,首先是4节点。先保存现在的restores栈的数量,因为如果4节点不在正确答案里的话,则需要对后面的移除进行恢复操作。并将4节点的行号加到answers中
cur_restores_count = len(restores)
selected_row = first_col.down
while selected_row != first_col:
answers.append(selected_row.row)
'''在这里进行后续操作'''
if dance_link_x(head, answers):
while len(restores): restores.pop()()
return True
answers.pop()
while len(restores) > cur_restores_count: restores.pop()()
selected_row = selected_row.down
可以看到上面的代码中间空了一部分,这里的意思是一顿操作后进行递归,如果递归的结果是返回True则说明找到了符合要求的答案,则恢复所有移除的节点,然后继续返回True,这里直接返回也可以,但是万一下次还要用这个链表就尴尬了,所有顺手给他恢复了
若果是False,说明4节点的行不在答案中,所以把4节点的行号从answers中弹出,并恢复到之前的链表。
下面详细说一下上面一顿操作的步骤
从4节点右边的第一个节点开始(5节点),对4节点所在行的节点进行遍历(不包括4节点),找到该节点的列节点,调用removeColumn,将该列从链表中移除
if selected_row.right != selected_row:
row_node = selected_row.right
while True:
col_node = row_node.col
col_node.removeColumn()
restores.append(col_node.restoreColumn)
'''这部分看后面'''
row_node = row_node.right
if row_node == selected_row.right: break
效果如下图所示:
接着又从该列节点往下,遍历列节点除外的每一个节点,并调用removeRow(如果节点所在行只有它一个就跳过)
col_node = col_node.down
while col_node != col_node.col:
if col_node.right != col_node:
col_node.right.removeRow()
restores.append(col_node.right.restoreRow)
col_node = col_node.down
下图是把C4列节点下面的14节点所在的行从链表中移除
再往下走没有更多节点了,又回到了C4节点,于是回到5节点,找到它的右一个节点:6节点。并重复上面的操作,先找点列节点C7,调用removeColumn,移除整列
然后从C7往下找,发现13节点所在行还有其他节点,将13节点所在行整航移除
回到6节点,再往右,发现遍历完毕,将被移除的节点从图中隐藏,整个链表如下所示:
然后就是之前所说的,对该链表进行递归,递归的结果如果是True则找到了答案,False则进行下一个尝试(10节点),如果都没有找到答案,这说明此种情况没有复合要求的解,全部恢复,返回False
while len(restores): restores.pop()()
return False
还有就是,递归的话就需要在函数的开头进行检查,所以回到函数的开始位置
首先检查Head节点的右(或左)节点是否指向自身,如果是则说明所有列已经被覆盖,返回True,否则说明还有列没有被覆盖
def dance_link_x(head: CrossCycleLinkNode, answers: list):
if head.right == head:
return True
接着要检查的就是是否有列节点的上(下)节点指向自身,如果有说明现在的情况无法得到复合要求的答案,直接return False
node = head.right
while True:
if node.down == node: return False
node = node.right
if node == head: break
以上就是每一步的详细过程,下面吧完整的代码贴出来:
def dance_link_x(head: CrossCycleLinkNode, answers: list):
if head.right == head: return True
node = head.right
while node != head:
if node.down == node: return False
node = node.right
restores = []
first_col = head.right
first_col.removeColumn()
restores.append(first_col.restoreColumn)
node = first_col.down
while node != first_col:
if node.right != node:
node.right.removeRow()
restores.append(node.right.restoreRow)
node = node.down
cur_restores_count = len(restores)
selected_row = first_col.down
while selected_row != first_col:
answers.append(selected_row.row)
if selected_row.right != selected_row:
row_node = selected_row.right
while True:
col_node = row_node.col
col_node.removeColumn()
restores.append(col_node.restoreColumn)
col_node = col_node.down
while col_node != col_node.col:
if col_node.right != col_node:
col_node.right.removeRow()
restores.append(col_node.right.restoreRow)
col_node = col_node.down
row_node = row_node.right
if row_node == selected_row.right: break
if dance_link_x(head, answers):
#while len(restores): restores.pop()()
return True
answers.pop()
while len(restores) > cur_restores_count: restores.pop()()
selected_row = selected_row.down
while len(restores): restores.pop()()
return False
下面是一初始化链表的辅助函数,也贴出来
def initCol(col_count: int):
head = CrossCycleLinkNode('head', 'column')
for i in range(col_count):
col_node = CrossCycleLinkNode(x=i, row=head.row)
col_node.right = head
col_node.left = head.left
col_node.right.left = col_node
col_node.left.right = col_node
return head
def appendRow(head: CrossCycleLinkNode, row_id: int, nums: list):
last = None
col = head.right
for num in nums:
while col != head:
if col.val == num:
node = CrossCycleLinkNode(1, row_id)
node.col = col
node.down = col
node.up = col.up
node.down.up = node
node.up.down = node
if last is not None:
node.left = last
node.right = last.right
node.left.right = node
node.right.left = node
last = node
break
col = col.right
else:
return
python跳舞的线_舞蹈链(Dance Link X)算法详解及python实现相关推荐
- AdaBoost算法详解与python实现
AdaBoost算法详解与python实现 https://tangshusen.me/2018/11/18/adaboost/
- RRT(Rapidly-Exploring Random Trees)算法详解及python实现
RRT(Rapidly-Exploring Random Trees)算法详解及python实现 前言 一.原理 二.伪代码 三.代码详解 总结 前言 快速探索随机树(RRT):作为一种随机数据结构, ...
- 排序算法(五)——堆排序算法详解及Python实现
本文目录 一.简介 二.算法介绍 三.代码实现 排序算法系列--相关文章 一.简介 堆排序(Heap Sort)算法,属于选择排序类,不稳定排序,时间复杂度O(nlogn). 堆排序由Floyd和Wi ...
- Simhash算法详解及python实现
Simhash算法详解及python实现 GoogleMoses Charikar发表的一篇论文"detecting near-duplicates for web crawling&quo ...
- kmeans算法详解和python代码实现
kmeans算法详解和python代码实现 kmeans算法 无监督学习和监督学习 监督学习: 是通过已知类别的样本分类器的参数,来达到所要求性能的过程 简单来说,就是让计算机去学习我们已经创建好了的 ...
- 编辑距离算法详解和python代码
编辑距离(Levenshtein Distance)算法详解和python代码 最近做NLP用到了编辑距离,网上学习了很多,看到很多博客写的有问题,这里做一个编辑距离的算法介绍,步骤和多种python ...
- python直线拟合_RANSAC算法详解(附Python拟合直线模型代码)
之前只是简单了解RANSAC模型,知道它是干什么的.然后今天有个课程设计的报告,上去讲了一下RANSAC,感觉这个东西也没那么复杂,所以今天就总结一些RASAC并用Python实现一下直线拟合. RA ...
- 敏感词或关键词过滤,DFA算法详解及python代码实现
一.前言 近期项目有了一个过滤敏感词的功能需求,在网上找了一些方法及解说,发现DFA算法比较好用,容易实现,但很多文章解释得不太清楚,这里将其详细描述,并用python代码实现. 二.DFA算法详解 ...
- python跳舞的线_【跳舞的线/教程】10分钟做一个可玩的饭制关卡
未经作者授权,禁止转载 相关游戏: 跳舞的线.Dancing Line.Unity 简介补充: 七月以后因为某些原因,我可能就不会再做饭制了--(之前挖的坑以后有可能会填,但是肯定有生之年了).最后打 ...
最新文章
- androidsettitle方法_Android使用setCustomTitle()方法自定义对话框标题
- 一起搞清楚 Spring Security 中的 UserDetails
- Python自动化开发学习15-css补充内容
- QMetaObject::connectSlotsByName: No matching signal for问题的解决方法
- YbtOJ#20068-[NOIP2020模拟赛B组Day5]连通子图【构造】
- im4java 文档_im4java学习---阅读documentation文档
- WCF的追踪分析工具——SvcPerf
- 从程序员到CTO的Java技术路线图 JAVA职业规划 JAVA职业发展路线图 系统后台框架图、前端工程师技能图 B2C电子商务基础系统架构解析...
- hbase java 教程_Hbase 教程-安装
- 计算机软考网络工程师历年真题,2019上半年软考网络工程师考试下午真题
- Deep Mind用AlphaZero开发国际象棋新规则-3!
- 赛效:超级简历在线简历助手教您一键制作简历
- 关于独立DFS和域DFS板书
- Adobe:Flash中存在高危零日漏洞
- 仁兄:腾讯区块链学习后的一些粗浅观点
- python 实现贷款计算
- TortoiseGit提示No supported authentication methods available异常
- 20220524 深度学习技术点
- C++:VS2017基本操作、番茄助手的重构功能以及C++的项目工程基本配置
- 使用Office Tool Plus+KMS部署Office365和Visio等工具
热门文章
- 利用云服务器和Python架设TCP Server控制ESP8266单片机
- 分布式训练 - 多机多卡 (DDP)
- 假设检验-单样本t检验
- 想进BAT一线互联网大厂,该怎么准备技术面试?一位6年老Android的面经总结(附300+面试题)
- 计算机科学与技术专业考数媒,数字媒体技术专业考研院校排名
- 计算机科学技术专业解析,计算机科学与技术专业怎么样 主要学什
- LVGL打印LOG日志
- iPad阅读应用横向评测: 普通2B文学青年的碰撞
- 计算机 图像处理 ei 期刊,【EA-ISET协会】中科院3区视觉图像处理类SCIEI源刊征稿...
- stm32独立看门狗