Fork me on GitHub

算法-03 | 深度优先DFS| 广度优先BFS | 剪枝优化

 

1. 搜索算法

在树(图/状态集)中寻找特定节点

深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构。

图上的搜索算法就是,在图中找出从一个顶点出发,到另一个顶点的路径。图上的搜索算法有深度优先、广度优先搜索算法,和A*A∗、IDA*IDA∗ 等启发式搜索算法。

广度优先搜索和深度优先搜索是图上的两种最常用、最基本的搜索算法,仅适用于状态空间不大的搜索。它们比A*A∗、IDA*IDA∗ 等启发式搜索算法要简单粗暴,没有什么优化,所以也叫作暴力搜索算法。

广度优先搜索,采用地毯式层层推进,从起始顶点开始,依次往外遍历。广度优先搜索需要借助队列来实现,遍历得到的路径就是起始顶点到终止顶点的最短路径。

深度优先搜索,采用回溯思想,适合用递归或栈来实现。遍历得到的路径并不是最短路径。

深度优先和广度优先搜索的时间复杂度都是 O(E),空间复杂度都是 O(V)。其中E代表边,O代表顶点。

搜索 -- 遍历

  • 每个节点都要访问一次
  • 每个节点仅仅要访问一次
  • 对于节点的访问顺序不限

    - 深度优先:depth first search
    - 广度优先:breadth first search

     还有是按照优先级优先进行搜索,(可按现实场景进行定义,更适合于现实中很多的业务场景。这种算法称为 启发式搜索

2. 深度优先搜索

“走迷宫”。假设你站在迷宫的某个岔路口,然后想找到出口。你随意选择一个岔路口来走,走着走着发现走不通的时候,你就回退到上一个岔路口,重新选择一条路继续走,直到最终找到出口。这种走法就是一种深度优先搜索策略。实线箭头表示遍历,虚线箭头表示回退。但深度优先搜索最先找出来的路径,并不是顶点 s 到顶点 t 的最短路径。深度优先搜索用的是回溯思想,回溯思想非常适合用递归来实现。

                                        

 

 

   黑色表起始点,红色表终点。按照左下右上的方向顺序走,即,如果左边可以走,我们先走左边。然后递归下去,没达到终点或者走不通了,回溯上一个位置......

  首先往左走,走不通了就回溯到上一步(左右都走过了再回溯)、上一步到起点,按左下右上的顺序走。

                                   

 

深度优先搜索 Depth-First-Search

遍历顺序 : 用栈

 

 

  

  

 DFS python代码模板

DFS 代码 - 递归写法
visited = set()
def dfs(node, visited):
    if node in visited: # terminator
        # already visited
        return
    visited.add(node)
    # process current node here.
    ...
    for next_node in node.children():
        if not next_node in visited:
            dfs(next_node, visited)
 

DFS 代码 - 非递归写法
def DFS(self, tree):
    if tree.root is None:
        return []
    visited, stack = [], [tree.root]
    while stack:
        node = stack.pop()
        visited.add(node)
        process (node)
        nodes = generate_related_nodes(node)
        stack.push(nodes)
    # other processing work
    ...
View Code

 

 

3. 广度优先搜索 Breadth-First-Search - BFS

广度优先搜索(Breadth-First-Search),简称 BFS。它是一种“地毯式”层层推进的搜索策略,即先查找离起始顶点最近的,然后是次近的,依次往外搜索:

广度优先搜索较之深度优先搜索之不同在于,深度优先搜索旨在不管有多少条岔路,先一条路走到底,不成功就返回上一个路口然后就选择下一条岔路,而广度优先搜索旨在面临一个路口时,把所有的岔路口都记下来,然后选择其中一个进入,然后将它的分路情况记录下来,然后再返回来进入另外一个岔路,并重复这样的操作。

       

顺序遍历: 用队列

 

 

  

  BFS的python代码模板:

BFS 代码
def BFS(graph, start, end):
    queue = []
    queue.append([start])
    visited.add(start)
    while queue:
        node = queue.pop()
        visited.add(node)
        process(node)
        nodes = generate_related_nodes(node)
        queue.push(nodes)
    # other processing work
    ...
 

DFS 代码 - 递归写法
visited = set()
def dfs(node, visited):
    visited.add(node)
    # process current node here.
    ...
    for next_node in node.children():
        if not next_node in visited:
            dfs(next node, visited)
View Code

 

 

https://www.geeksforgeeks.org/difference-between-bfs-and-dfs/

4. DFS| BFS 与 Tree的遍历的关系

A Tree is typically traversed in two ways:

                                                                    

 

 

BFS and DFSs of above Tree

Breadth First Traversal : 1 2 3 4 5

Depth First Traversals:
      Preorder Traversal : 1 2 4 5 3 
      Inorder Traversal  :  4 2 5 1 3 
      Postorder Traversal : 4 5 2 3 1

All four traversals require O(n) time as they visit every node exactly once.

here is difference in terms of extra space required.

  1. Extra Space required for Level Order Traversal is O(w) where w is maximum width of Binary Tree. In level order traversal, queue one by one stores nodes of different level.
  2. Extra Space required for Depth First Traversals is O(h) where h is maximum height of Binary Tree. In Depth First Traversals, stack (or function call stack) stores all ancestors of a node.

 

https://xiaqiu2233.github.io/2017/10/04/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B9%BF%E5%BA%A6%E5%92%8C%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E9%81%8D%E5%8E%86%EF%BC%88%E5%85%88%E5%BA%8F%E3%80%81%E4%B8%AD%E5%BA%8F%E3%80%81%E5%90%8E%E5%BA%8F%EF%BC%89/

 

https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/

 

5. 高级搜索

初级搜索(傻搜,暴力搜索):
1. 朴素搜索
2. 优化方式:不重复(fibonacci)、剪枝(生成括号问题)
    如斐波拉契问题;
    所谓剪枝就是整个状态树这个分支没有必要的时候就把它剪掉,不进行搜索。
3. 搜索方向:
    DFS: depth first search 深度优先搜索 (傻子搜索, 一路不回头直到撞了南墙)
    BFS: breadth first search 广度优先搜索 (距离最近的)
    双向搜索(从起点和终点分别做一个广度优先,然后在中间相遇,它的时间更快)、
    启发式搜索也叫A*算法或优先级搜索(不是用栈或队列,而是用一个优先队列放里边,优先队列是按照这个结的优先级,有些结点更可能会达到我们需要的结果,先就把它从队列中拿出来进行搜索)
    
    之所以分DFS和BFS是因为计算机数据结构里有一个先入后出的栈和 先入先出的队列;

 双向BFS

Breadth First Search(BFS)

   

  Breadth First Search Levels

     

Two-ended BFS 双向BFS

  A和L各从两边扩散,直到两者相遇,A、L各自所走的路径和。

启发式搜索

启发式搜索 Heuristic Search(A*) 也叫智能搜索 或者 根据某项条件来不断地优化搜索的方向。

一边搜索一边思考 <--> 也叫思考型搜索 ; 

通过优先级不断地去找要找的点,先用优先级高的拿出来搜索;

启发式搜索它也是基于BFS的。

 

A* 也是基于 BFS 代码
def BFS(graph, start, end):
  queue = []
  queue.append([start])
  visited.add(start)
while queue:
  node = queue.pop() #queue每次都pop一个最近的元素, can we add more intelligence here ? 在pop node时多加一些智能的元素在里面,不再使用queue的傻子似的先入先出。可以使用优先队列。
  visited.add(node)

  process(node)   nodes
= generate_related_nodes(node)   queue.push(nodes)
A* search的模板如下:
def AstarSearch(graph, start, end):   pq = collections.priority_queue() # 优先级 —> 估价函数   pq.append([start])   visited.add(start)   while pq:     node = pq.pop() # can we add more intelligence here ?     visited.add(node)
    process(node)     nodes
= generate_related_nodes(node)     unvisited = [node for node in nodes if node not in visited]     pq.push(unvisited)

使用了PQ即priority queue;

 

posted @ 2020-07-19 19:26  kris12  阅读(1920)  评论(0编辑  收藏  举报
levels of contents