谈一谈|递归解析之DFS全排列
前言
通过上一篇文章《return None来看递归函数流程解析》了解了递归函数的调用及执行之后,来看看如何应用吧。本篇文章将以DFS算法实现全排列为例,加深对递归的理解,顺便看看DFS算法中回溯(回退)机制的原理。
DFS算法及全排列理论简述
在正式开始之前,先来看看DFS算法及全排列的理论描述,如果对这部分已经掌握的读者,可以直接看下一个部分。
DFS算法通俗的讲就是一条道走到黑,再回头。以其典型的应用走迷宫为例。先选择一条路一直走下去,当走不通了,就回到上一个路口,看看还有没有其他可以走,有就继续往下走,没有就再倒退一个路口,直到走出迷宫或者走完所有路线。
全排列简单地说就是列出一个集合内所有元素的排列组合情况,高中知识就不赘述。那全排列如何与DFS算法结合呢?
DFS算法求全排列
以下题为例:
求出1,2,3,4四个元素的全排列
1 分析
全排列的所有情况可以用树状图表示出来,图一中的红色数字1234便是其中一种排列情况。
图一 全排列示意图
树状图也是图,根据DFS算法的思想,完全可以把图一视为一个迷宫,只是需要找的不是迷宫的出口,而是要列出所有迷宫路径的情况。
当在第一个路口选择1,那么第二个路口还有2、3、4可以选择。如果第一个路口选择2,那么第二个路口可以选择1、3、4中的一个。总共需要选择4次,也就是4个路口。
2 算法描述
根据DFS算法思想,一条道走到黑,走不通了再倒回去。
图二 DFS算法思维流程图
将4个格子填满数字,如图二中的a。执行步骤2
清空当前格子(后退一格),执行步骤3
查看有没有其他没用过的数字可以填充下一个空白格子,没有就再次执行步骤2,如图二中的b、c。有就填充,并再次执行步骤3.直到格子填满,如图二中的d、c。当格子填满再次执行步骤2
3 代码实现
如图三,对于需要排列的元素用数组arr储存,temp用于保存结果。由于选择一个数字后,后面不可再选,如temp第一个格子填1,后面三个格子便不能再填1,所以需要有visit记录哪些元素可以使用,True表示可以使用,Flase表示已经使用过,不能再使用。
图三 相关变量示意图
visit = [True, True, True, True]
temp = ["" for x in range(0, 4)]
#position表示需要对temp哪个位置进行填充
def dfs(position):
# 递归出口,temp数组已填满
if position == len(arr):
print(temp)
return
# 递归主体
for index in range(0, len(arr)):
#当前元素可以使用,填入temp
if visit[index] == True:
temp[position] = arr[index]
#填入后将其状态改为不可使用
visit[index] = False
dfs(position + 1)
visit[index] = True # 回溯。非常重要
4 代码解析
图四 DFS全排列代码执行示意图
1)执行dfs(0),注意函数中的第一层for循环,表示对于temp[0],会分别填入1、2、3、4。以1为例,当temp[0]=1,visit[0]=Flase,执行dfs(0+1).
visit[index] = True 还未执行,先不管。如图4中的一
2)执行dfs(1),一样有for循环,依次遍历1、2、3、4。由于visit[0]=Flase,所以1不可选,跳过。visit[1]=True,令temp[1]=2, 执行dfs(1+1).如图4中的二
3)执行dfs(2),同理得到temp[2]=3,执行dfs(3),得到temp[3]=4。执行def(4),由于position == len(arr),递归出口,输出temp=[1,2,3,4]。如图4中的三、四、五。
4)输出后,执行return语句,返回到dfs(3),并继续从dfs(position+1)语句往下,执行visit[index] = True 语句,dfs(3)的代码全部执行完毕,回到dfs(2)。如图4中的黑线A、B.这时visit=[Flase,Flase,Flase,True]
5)dfs(2)在执行完第三个for循环后visit=[Flase,Flase,True,True],还差第四个循环未执行,令visit[2]=4,同理执行dfs(3),令visit[3]=3,又得到一种排列情况temp=[1,2,4,3],之后就回溯到dfs(1)在往下得出[1,3,2,4]、[1,3,4,2]等等。
总结
递归函数在实际应用中一定要理解其调用执行流程,才能得心应手,少犯错。看完这篇文章的读者可以试试在自己脑中推理dfs全排列的流程吧。
- Spring RestFul and RestTemplate
- Spring boot with Velocity template
- Oracle 11g R2 RAC 高可用连接特性 – SCAN 详解
- Spring boot · 链接池配置
- Linux下命令行图片格式转换
- 用SQL解一道有趣的数学题:Gauss和Poincare
- OpenSSL 转换证书格式
- 电子邮件服务器DKIM配置
- 自相矛盾:一个进程可以自成死锁么?
- 数据库安全·开发加密插件
- wordpress无法安装这个包。: PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file 'C:WINDOWSTEMP/wordpress-4.
- 数据库安全·数据加密
- Medium网友开发了一款应用程序 让学习算法和数据结构变得更有趣
- 数据库安全·Token 认证
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Excel VBA常用功能加载宏——打开活动工作簿所在文件夹
- 常用功能加载宏——拆分工作表
- MyVBA加载宏——添加自定义菜单04——功能实现
- CS学习笔记 | 14、powerup提权的方法
- VBA解压缩ZIP文件05——Huffman树
- JavaScript|jQuery基础语法
- VBA解压缩ZIP文件03——解压准备工作
- VBA解压缩ZIP文件04——解析ZIP文件结构
- 开发|Springboot简单实现文件上传
- VBA解压缩ZIP文件01——实现的功能
- MyVBA加载宏——添加自定义菜单03——功能分析
- MyVBA加载宏——添加自定义菜单02——给按钮添加单击事件
- 科研猫小课堂:敲黑板!竞争风险模型应该如何分析?
- 常用功能加载宏——快速定位合并单元格
- 常用功能加载宏——调用微信截图