谈一谈|递归解析之DFS全排列

时间:2022-07-22
本文章向大家介绍谈一谈|递归解析之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全排列的流程吧。