【Python算法】遍历(Traversal)、深度优先(DFS)、广度优先(BFS)

图结构:

  非常强大的结构化思维(或数学)模型。如果您能用图的处理方式来规范化某个问题,即使这个问题本身看上去并不像个图问题,也能使您离解决问题更进一步。

  在众多图算法中,我们常会用到一种非常实用的思维模型--遍历(traversal):对图中所有节点的探索及访问操作。

图的一些相关概念:

  简单图(Simple graph):无环并且无平行边的图.

  路(path):内部点互不相同的链。

  如果无向图G中每一对不同的顶点x和y都有一条路,(即W(G)=1,连通分支数)则称G是连通图,反之称为非连通图。

  两端点相同的路(即闭路)称为圈(cycle)。

  树(tree)是无圈连通无向图。树中度数为1的结点称为树的叶结点。树中度数大于1的结点称为树的分支节点或内部结点。

  不相交的若干树称为森林(forest),即森林的每个连通分支是树。

  定理1:T是树<=>T中无环,且任何不同两顶点间有且仅有一条路。

  定理2:T是树<=>T连通且|e|=n-1,|e|为T的边数,n为T的顶点数。

  由根到某一顶点v的有向路的长度,称为顶点v的层数(level)。根树的高度就是顶点层数的最大值。

深度优先搜索:

  求连通简单图G的一棵生成树的许多方法中,深度优先搜索(depth first search)是一个十分重要的算法。

基本思想:

  任意选择图G的一个顶点V0作为根,通过相继地添加边来形成在顶点V0开始的路,其中每条新边都与路上的最后一个顶点以及不在路上的一个顶点相关联。

  继续尽可能多地添加边到这条路。若这条路经过图G的所有顶点,则这条路即为G的一棵生成树;

  若这条路没有经过G的所有顶点,不妨设形成这条路的顶点顺序V0,V1,......,Vn。则返回到路里的次最后顶点V(n-1).

    若有可能,则形成在顶点v(n-1)开始的经过的还没有放过的顶点的路;

    否则,返回到路里的顶点v(n-2)。

  然后再试。重复这个过程,在所访问过的最后一个顶点开始,在路上次返回的顶点,只要有可能就形成新的路,直到不能添加更多的边为止。

  深度优先搜索也称为回溯(back tracking)

栗子:

  用深度优先搜索来找出图3-9所示图G的生成树,任意地从顶点d开始,生成步骤显示在图3-10。

  

广度优先搜索:

  可用广度优先搜索(breadth first search)来产生连通简单图的生成树。

基本思想:

  从图的顶点中任意第选择一个根,然后添加与这个顶点相关联的所有边,在这个阶段添加的新顶点成为生成树里1层上的顶点,任意地排序它们。

  下一步,按照顺序访问1层上的每一个顶点,只要不产生回路,就添加与这个顶点相关联的每个边。这样就产生了树里2的上的顶点。遵循同样的原则继续下去,经有限步骤就产生了生成树。

栗子:

  用广度优先搜索找出图3-9所示图G的生成树,选择顶点f作为根:

  

两种著名的基本遍历策略:

  深度优先搜索(depth-first search)

  广度优先搜索(breadth-first search)

找出图的连通分量:

  如果一个图中的任何一个节点都有一条路径可以到达其他各个节点,那么它就是连通的。

  连通分量:目标图中最大(且独立)的连通子图。

  从图中的某个部分开始,逐步扩大其连通子图的确认范围,直至它再也无法向外连通为止。

def walk(G,s,S=set()):
    P,Q=dict(),set()
    P[s]=None                    # s节点没有前任节点
    Q.add(s)                     # 从s开始搜索
    while Q:
        u=Q.pop()
        for v in G[u].difference(P,S):   # 得到新节点
            Q.add(v)
            P[v]=u               # 记录前任节点
    return P

def components(G):
    comp = []
    seen = set()
    for u in range(9):
        if u in seen: continue
        C = walk(G, u)
        seen.update(C)
        comp.append(C)
    return comp

if __name__ == "__main__":
    a, b, c, d, e, f, g, h, i= range(9)
    N = [
        {b, c, d},   # a
        {a, d},      # b
        {a,d},       # c
        {a,c,d},     # d
        {g,f},       # e
        {e,g},       # f
        {e,f},       # g
        {i},         # h
        {h}          # i
    ]
    comp = components(N)
    print(comp)

深度优先搜索:

  

  递归版的深度优先搜索 :

def rec_dfs(G,s,S=None):
    if S is None:S = set()
    S.add(s)
    for u in G[s]:
        if u in S:coontinue
        rec_dfs(G,u,S)

  迭代版的深度优先搜索 :

def iter_dfs(G,s):
    S,Q=set(),[]
    Q.append(s)
    while Q:
        u = Q.pop()
        if u in S:continue
        S.add(u)
        Q.extend(G[u])
        yield u

if __name__ == "__main__":
    a, b, c, d, e, f, g, h, i = range(9)
    G = [{b, c, d, e, f},  # a
         {c, e},           # b
         {d},              # c
         {e},              # d
         {f},              # e
         {c, g, h},        # f
         {f, h},           # g
         {f, g}            # h
         ]
    print(list(iter_dfs(G,a)))       # [0, 5, 7, 6, 2, 3, 4, 1]

  通用性的图遍历函数

def traverse(G,s,qtype=set()):
    S,Q=set(),qtype()
    Q.add(s)
    while Q:
        u=Q.pop()
        if u in S:continue
        S.add(u)
        for v in G[u]:
            Q.add(v)
        yield u  

class stack(list):
    add=list.append  

g=list(traverse(G,0,stack))  

基于深度优先搜索的拓扑排序

  

def dfs_topsort(G):
    S,res=set(),[]
    def recurse(u):
        if u in S: return
        S.add(u)
        for v in G[u]:
            recurse(v)
        res.append(u)
    for u in G:
        recurse(u)
    res.reverse()
    return res

if __name__=="__main__":
    a, b, c, d, e, f, g, h, i = range(9)
    G = {
        ‘a‘: set(‘bf‘),
        ‘b‘: set(‘cdf‘),
        ‘c‘: set(‘d‘),
        ‘d‘: set(‘ef‘),
        ‘e‘: set(‘f‘),
        ‘f‘: set(‘‘)
    }
    res = dfs_topsort(G)

迭代深度的深度优先搜索

def iddfs(G,s):
    yielded=set()
    def recurse(G,s,d,S=None):
        if s not in yielded:
            yield s
            yielded.add(s)
        if d==0:return
        if S is None:S=set()
        S.add(s)
        for u in G[s]:
            if u in S:continue
            for v in recurse(G,u,d-1,S):
                yield v
    n=len(G)
    for d in range(n):
        if len(yielded)==n:break
        for u in recurse(G,s,d):
            yield u  

if __name__=="__main__":
    a, b, c, d, e, f, g, h, i= range(9)
    N = [
        {b, c, d},  # a
        {a, d},     # b
        {a,d},      # c
        {a,b,c},    # d
        {g,f},      # e
        {e,g},      # f
        {e,f},      # g
        {i},        # h
        {h}         # i
    ]  

    G = [{b,c,d,e,f},#a
        {c,e},      # b
        {d},        # c
        {e},        # d
        {f},        # e
        {c,g,h},    # f
        {f,h},      # g
        {f,g}       # h
   ]  

    p=list(iddfs(G,0))         # [0, 1, 2, 3, 4, 5, 6, 7]
    m=list(iddfs(N,0))         # [0, 1, 2, 3]  

广度优先搜索

import collections
def bfs(G,s):
    P,Q={s:None},collections.deque([s])
    while Q:
        u=Q.popleft()
        for v in G[u]:
            if v in P:continue
            P[v]=u
            Q.append(v)
    return P  

强连通分量

  如果有向图的任何一对结点间是相互可达的,则称这个有向图是强连通的

def tr(G):
    GT={}
    for u in G:GT[u]=set()
    for u in G:
        for v in G[u]:
            GT[v].add(u)
    return GT
def scc(G):
    GT=tr(G)
    sccs,seen=[],set()
    for u in dfs_topsort(G):
        if u in seen:continue
        C=walk(GT,u,seen)
        seen.update(C)
        sccs.append(C)
    return sccs  

def dfs_topsort(G):
    S,res=set(),[]
    def recurse(u):
        if u in S:return
        S.add(u)
        for v in G[u]:
            recurse(v)
        res.append(u)
    for u in G:
        recurse(u)
    res.reverse()
    return res  

def walk(G,s,S=set()):
    P,Q=dict(),set()
    P[s]=None
    Q.add(s)
    while Q:
        u=Q.pop()
        print("u: ",u)
        print("S:",S)
        for v in G[u].difference(P,S):
            Q.add(v)
            P[v]=u
    return P  

if __name__=="__main__":
    a, b, c, d, e, f, g, h, i= range(9)  

    G={
        ‘a‘:set(‘bc‘),
        ‘b‘:set(‘edi‘),
        ‘c‘:set(‘d‘),
        ‘d‘:set(‘ah‘),
        ‘e‘:set(‘f‘),
        ‘f‘:set(‘g‘),
        ‘g‘:set(‘eh‘),
        ‘h‘:set(‘i‘),
        ‘i‘:set(‘h‘)
        }
    sccs=scc(G)
    # [{‘a‘: None, ‘d‘: ‘a‘, ‘c‘: ‘d‘, ‘b‘: ‘d‘}, {‘e‘: None, ‘g‘: ‘e‘, ‘f‘: ‘g‘}, {‘h‘: None, ‘i‘: ‘h‘}]  
时间: 2024-11-05 13:46:31

【Python算法】遍历(Traversal)、深度优先(DFS)、广度优先(BFS)的相关文章

图的遍历之深度优先和广度优先

图的遍历之深度优先和广度优先 深度优先遍历 假设给定图G的初态是所有顶点均未曾访问过.在G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过:然后依次从v出发搜索v的每个邻接点w.若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止.若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止. 图的深度优先遍历类似于树

图的遍历(深度优先与广度优先搜索两种方案)

1.图的遍历--深度优先搜索 import java.util.Scanner ; public class Map{ static int n ; static int m ; static int[] book ; static int[][] e ; public static void mapDfs(int cur){ //深度优先搜索思想核心: System.out.print(cur + " ") ; for (int i=1;i<=n;i++) { if (e[cu

UVA 548.Tree-fgets()函数读入字符串+二叉树(中序+后序遍历还原二叉树)+DFS or BFS(二叉树路径最小值并且相同路径值叶子节点权值最小)

Tree UVA - 548 题意就是多次读入两个序列,第一个是中序遍历的,第二个是后序遍历的.还原二叉树,然后从根节点走到叶子节点,找路径权值和最小的,如果有相同权值的就找叶子节点权值最小的. 最后输出来叶子节点. 一开始写的时候是用gets读入的,报CE, 要用fgets写,关于fgets(),传送门: fgets函数及其用法,C语言fgets函数详解 一开始用bfs过的,后来发现,好多人都是dfs过的,又写了一下dfs... 代码: 1 //二叉树的中序和后序遍历还原树并输出最短路径的叶子

数据结构:图的遍历--深度优先、广度优先

图的遍历:深度优先.广度优先 遍历 图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点.当然,每个顶点有且只能被访问一次. 在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式.这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的.先看下两种遍历方式的遍历规则: 深度优先 深度优先遍历也叫深度优先搜索(Depth First Search).它的遍历规则:不断地沿着顶点的深度方向遍历.顶点的深度方向是指它的邻接点方向. 具体点,给定一图G=<V,E>,

图的遍历之深度优先搜索和广度优先搜索

转自:http://www.cnblogs.com/skywang12345/ 深度优先搜索的图文介绍 1. 深度优先搜索介绍 图的深度优先搜索(Depth First Search),和树的先序遍历比较类似. 它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到. 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访

深度优先与广度优先算法

图的遍历有深度优先和广度优先算法. 深度优先遍历可描述为一个递归算法.当到达顶点v时,具体操作是: ①访问(v); ②for(与v相邻的每个顶点w) 遍历(w): //深度优先算法 template<int max_size> void Diagraph<max_size>::depth_first(void(*visit)(Vertex &)) const { bool visited[max_size]; //引入数组防止无限循环 Vertex v; for (all

二叉树的遍历之 深度优先(DFS)和广度优先(BFS)

深度优先遍历: 深度优先搜索算法(Depth First Search),是搜索算法的一种.是沿着树的深度遍历树的节点,尽可能深的搜索树的分支. 当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点.这一过程一直进行到已发现从源节点可达的所有节点为止. 如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止. 如图所示的二叉树: A 是第一个访问的,然后顺序是 B.D,然后是 E.接着再是 C.F.G. 那么,怎么样才能来保证

图的遍历算法:DFS、BFS

在图的基本算法中,最初需要接触的就是图的遍历算法,根据访问节点的顺序,可分为深度优先搜索(DFS)和广度优先搜索(BFS). DFS(深度优先搜索)算法 Depth-First-Search 深度优先算法,是一种用于遍历或搜索树或图的算法.沿着树的深度遍历树的节点,尽可能深的搜索树的分支. 当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点. 这一过程一直进行到已发现从源节点可达的所有节点为止. 如果还存在未被发现的节点, 则选择其中一个作为源节点并重复以上过程,整个进程反复

算法学习笔记 二叉树和图遍历—深搜 DFS 与广搜 BFS

图的深搜与广搜 马上又要秋招了,赶紧复习下基础知识.这里复习下二叉树.图的深搜与广搜.从图的遍历说起,图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其经典应用走迷宫.N皇后.二叉树遍历等.遍历即按某种顺序访问"图"中所有的节点,顺序分为: 深度优先(优先往深处走),用的数据结构是栈, 主要是递归实现: 广度优先(优先走最近的),用的数据结构是队列,主要是迭代实现: 对于深搜,由于递归往往可以方便的利

算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS

图的深搜与广搜 复习下二叉树.图的深搜与广搜. 从图的遍历说起.图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其经典应用走迷宫.N皇后.二叉树遍历等.遍历即按某种顺序訪问"图"中全部的节点,顺序分为: 深度优先(优先往深处走),用的数据结构是栈, 主要是递归实现. 广度优先(优先走近期的).用的数据结构是队列.主要是迭代实现. 对于深搜.因为递归往往能够方便的利用系统栈,不须要自己维护栈.所以通常实