常见算法6、深度优先搜索 Depth-First-Search

一、简介

1、定义

深度优先搜索(Depth-First-Search)是图的搜索算法之一,是一个针对图和树的遍历算法,英文缩写为DFS。过程简要来说就是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先搜索的概念。

2、与深度优先搜索的比较

我们假设一个节点衍生出来的相邻节点平均的个数是N个,那么当起点开始搜索的时候,队列有一个节点,当起点拿出来后,把它相邻的节点放进去,那么队列就有N个节点,当下一层的搜索中再加入元素到队列的时候,节点数达到了N²,你可以想想,一旦N是一个比较大的数的时候,这个树的层次又比较深,那这个队列就得需要很大的内存空间了。

于是广度优先搜索的缺点出来了:在树的层次较深&子节点数较多的情况下,消耗内存十分严重。广度优先搜索适用于节点的子节点数量不多,并且树的层次不会太深的情况。

那么深度优先就可以克服这个缺点,因为每次搜的过程,每一层只需维护一个节点。但回过头想想,广度优先能够找到最短路径,那深度优先能否找到呢?深度优先的方法是一条路走到黑,那显然无法知道这条路是不是最短的,所以你还得继续走别的路去判断是否是最短路?

于是深度优先搜索的缺点也出来了:难以寻找最优解,仅仅只能寻找有解。其优点就是内存消耗小,克服了刚刚说的广度优先搜索的缺点。

二、实现原理

深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形。

1、把根节点压入栈中。
2、每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。

如图:

  • 假设选定节点A为根节点,将根节点A放入栈中。从栈中取出节点A,寻找到A的子节点B、C并放入栈中。(此时处于节点A)
  • 从栈中取出节点B,寻找B的子节点D,放入栈中。(此时处于节点B)
  • 取出节点D,寻找子节点F并放入栈中。
  • 下一步取出节点F重复执行以上操作,直至遍历全图。

注:

当一个节点有多个子节点,子节点入栈的顺序是随机的,也就是说同样一个图,可以产生多种结果。
走到节点F时,会发现F没有子节点,那么此时就会回跳到F的父节点,并寻找一个尚未走过的节点(若父节点没有尚未走过的子节点,则继续回跳),回跳的过程可以不用表述。

三、代码实现:

1、Python 3 :

graph = {
        'A':['B','C'],
        'B':['A','C','D'],
        'C':['A','B','D','E'],
        'D':['B','C','E','F'],
        'E':['C','D'],
        'F':['D']
        }

def DFS(graph,start):
    stack = list(start) #将起始节点放入栈
    closed = set() #创建一个集合,存放已经走过的节点
    closed.add(start)
    while(len(stack)>0):
        vertex = stack.pop() #从栈取出一个节点
        nodes = graph[vertex]
        #判断节点是否走过
        for node in nodes:  
            if node not in closed:
                #若节点没有走过,则放入栈与集合
                stack.append(node) 
                closed.add(node)
        print(vertex,end='\t')

DFS(graph,'A') 
=============== 运行结果:===============
A   C   E   D   F   B

参考

1、《算法图解》https://www.manning.com/books/grokking-algorithms

你可能感兴趣的:(常见算法6、深度优先搜索 Depth-First-Search)