这两天打算做个数独玩玩,查了一下解数独最好的算法叫舞蹈链: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实现相关推荐

  1. AdaBoost算法详解与python实现

    AdaBoost算法详解与python实现 https://tangshusen.me/2018/11/18/adaboost/

  2. RRT(Rapidly-Exploring Random Trees)算法详解及python实现

    RRT(Rapidly-Exploring Random Trees)算法详解及python实现 前言 一.原理 二.伪代码 三.代码详解 总结 前言 快速探索随机树(RRT):作为一种随机数据结构, ...

  3. 排序算法(五)——堆排序算法详解及Python实现

    本文目录 一.简介 二.算法介绍 三.代码实现 排序算法系列--相关文章 一.简介 堆排序(Heap Sort)算法,属于选择排序类,不稳定排序,时间复杂度O(nlogn). 堆排序由Floyd和Wi ...

  4. Simhash算法详解及python实现

    Simhash算法详解及python实现 GoogleMoses Charikar发表的一篇论文"detecting near-duplicates for web crawling&quo ...

  5. kmeans算法详解和python代码实现

    kmeans算法详解和python代码实现 kmeans算法 无监督学习和监督学习 监督学习: 是通过已知类别的样本分类器的参数,来达到所要求性能的过程 简单来说,就是让计算机去学习我们已经创建好了的 ...

  6. 编辑距离算法详解和python代码

    编辑距离(Levenshtein Distance)算法详解和python代码 最近做NLP用到了编辑距离,网上学习了很多,看到很多博客写的有问题,这里做一个编辑距离的算法介绍,步骤和多种python ...

  7. python直线拟合_RANSAC算法详解(附Python拟合直线模型代码)

    之前只是简单了解RANSAC模型,知道它是干什么的.然后今天有个课程设计的报告,上去讲了一下RANSAC,感觉这个东西也没那么复杂,所以今天就总结一些RASAC并用Python实现一下直线拟合. RA ...

  8. 敏感词或关键词过滤,DFA算法详解及python代码实现

    一.前言 近期项目有了一个过滤敏感词的功能需求,在网上找了一些方法及解说,发现DFA算法比较好用,容易实现,但很多文章解释得不太清楚,这里将其详细描述,并用python代码实现. 二.DFA算法详解 ...

  9. python跳舞的线_【跳舞的线/教程】10分钟做一个可玩的饭制关卡

    未经作者授权,禁止转载 相关游戏: 跳舞的线.Dancing Line.Unity 简介补充: 七月以后因为某些原因,我可能就不会再做饭制了--(之前挖的坑以后有可能会填,但是肯定有生之年了).最后打 ...

最新文章

  1. androidsettitle方法_Android使用setCustomTitle()方法自定义对话框标题
  2. 一起搞清楚 Spring Security 中的 UserDetails
  3. Python自动化开发学习15-css补充内容
  4. QMetaObject::connectSlotsByName: No matching signal for问题的解决方法
  5. YbtOJ#20068-[NOIP2020模拟赛B组Day5]连通子图【构造】
  6. im4java 文档_im4java学习---阅读documentation文档
  7. WCF的追踪分析工具——SvcPerf
  8. 从程序员到CTO的Java技术路线图 JAVA职业规划 JAVA职业发展路线图 系统后台框架图、前端工程师技能图 B2C电子商务基础系统架构解析...
  9. hbase java 教程_Hbase 教程-安装
  10. 计算机软考网络工程师历年真题,2019上半年软考网络工程师考试下午真题
  11. Deep Mind用AlphaZero开发国际象棋新规则-3!
  12. 赛效:超级简历在线简历助手教您一键制作简历
  13. 关于独立DFS和域DFS板书
  14. Adobe:Flash中存在高危零日漏洞
  15. 仁兄:腾讯区块链学习后的一些粗浅观点
  16. python 实现贷款计算
  17. TortoiseGit提示No supported authentication methods available异常
  18. 20220524 深度学习技术点
  19. C++:VS2017基本操作、番茄助手的重构功能以及C++的项目工程基本配置
  20. 使用Office Tool Plus+KMS部署Office365和Visio等工具

热门文章

  1. 利用云服务器和Python架设TCP Server控制ESP8266单片机
  2. 分布式训练 - 多机多卡 (DDP)
  3. 假设检验-单样本t检验
  4. 想进BAT一线互联网大厂,该怎么准备技术面试?一位6年老Android的面经总结(附300+面试题)
  5. 计算机科学与技术专业考数媒,数字媒体技术专业考研院校排名
  6. 计算机科学技术专业解析,计算机科学与技术专业怎么样 主要学什
  7. LVGL打印LOG日志
  8. iPad阅读应用横向评测: 普通2B文学青年的碰撞
  9. 计算机 图像处理 ei 期刊,【EA-ISET协会】中科院3区视觉图像处理类SCIEI源刊征稿...
  10. stm32独立看门狗