小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【Python算法】遍歷(Traversal)、深度優(yōu)先(DFS)、廣度優(yōu)先(BFS)

 highoo 2019-03-20

圖結(jié)構(gòu):

  非常強(qiáng)大的結(jié)構(gòu)化思維(或數(shù)學(xué))模型。如果您能用圖的處理方式來(lái)規(guī)范化某個(gè)問(wèn)題,即使這個(gè)問(wèn)題本身看上去并不像個(gè)圖問(wèn)題,也能使您離解決問(wèn)題更進(jìn)一步。

  在眾多圖算法中,我們常會(huì)用到一種非常實(shí)用的思維模型--遍歷(traversal):對(duì)圖中所有節(jié)點(diǎn)的探索及訪問(wèn)操作。

 

圖的一些相關(guān)概念:

  簡(jiǎn)單圖(Simple graph):無(wú)環(huán)并且無(wú)平行邊的圖.

  路(path):內(nèi)部點(diǎn)互不相同的鏈。

  如果無(wú)向圖G中每一對(duì)不同的頂點(diǎn)x和y都有一條路,(即W(G)=1,連通分支數(shù))則稱(chēng)G是連通圖,反之稱(chēng)為非連通圖。

  兩端點(diǎn)相同的路(即閉路)稱(chēng)為圈(cycle)。

  樹(shù)(tree)是無(wú)圈連通無(wú)向圖。樹(shù)中度數(shù)為1的結(jié)點(diǎn)稱(chēng)為樹(shù)的葉結(jié)點(diǎn)。樹(shù)中度數(shù)大于1的結(jié)點(diǎn)稱(chēng)為樹(shù)的分支節(jié)點(diǎn)或內(nèi)部結(jié)點(diǎn)。

  不相交的若干樹(shù)稱(chēng)為森林(forest),即森林的每個(gè)連通分支是樹(shù)。

  定理1:T是樹(shù)<=>T中無(wú)環(huán),且任何不同兩頂點(diǎn)間有且僅有一條路。

  定理2:T是樹(shù)<=>T連通且|e|=n-1,|e|為T(mén)的邊數(shù),n為T(mén)的頂點(diǎn)數(shù)。

  由根到某一頂點(diǎn)v的有向路的長(zhǎng)度,稱(chēng)為頂點(diǎn)v的層數(shù)(level)。根樹(shù)的高度就是頂點(diǎn)層數(shù)的最大值。

 

深度優(yōu)先搜索:

  求連通簡(jiǎn)單圖G的一棵生成樹(shù)的許多方法中,深度優(yōu)先搜索(depth first search)是一個(gè)十分重要的算法。

基本思想:

  任意選擇圖G的一個(gè)頂點(diǎn)V0作為根,通過(guò)相繼地添加邊來(lái)形成在頂點(diǎn)V0開(kāi)始的路,其中每條新邊都與路上的最后一個(gè)頂點(diǎn)以及不在路上的一個(gè)頂點(diǎn)相關(guān)聯(lián)。

  繼續(xù)盡可能多地添加邊到這條路。若這條路經(jīng)過(guò)圖G的所有頂點(diǎn),則這條路即為G的一棵生成樹(shù);

  若這條路沒(méi)有經(jīng)過(guò)G的所有頂點(diǎn),不妨設(shè)形成這條路的頂點(diǎn)順序V0,V1,......,Vn。則返回到路里的次最后頂點(diǎn)V(n-1).

    若有可能,則形成在頂點(diǎn)v(n-1)開(kāi)始的經(jīng)過(guò)的還沒(méi)有放過(guò)的頂點(diǎn)的路;

    否則,返回到路里的頂點(diǎn)v(n-2)。

  然后再試。重復(fù)這個(gè)過(guò)程,在所訪問(wèn)過(guò)的最后一個(gè)頂點(diǎn)開(kāi)始,在路上次返回的頂點(diǎn),只要有可能就形成新的路,直到不能添加更多的邊為止。

  深度優(yōu)先搜索也稱(chēng)為回溯(back tracking)

栗子:

  用深度優(yōu)先搜索來(lái)找出圖3-9所示圖G的生成樹(shù),任意地從頂點(diǎn)d開(kāi)始,生成步驟顯示在圖3-10。

  

 

廣度優(yōu)先搜索:

  可用廣度優(yōu)先搜索(breadth first search)來(lái)產(chǎn)生連通簡(jiǎn)單圖的生成樹(shù)。

基本思想:

  從圖的頂點(diǎn)中任意第選擇一個(gè)根,然后添加與這個(gè)頂點(diǎn)相關(guān)聯(lián)的所有邊,在這個(gè)階段添加的新頂點(diǎn)成為生成樹(shù)里1層上的頂點(diǎn),任意地排序它們。

  下一步,按照順序訪問(wèn)1層上的每一個(gè)頂點(diǎn),只要不產(chǎn)生回路,就添加與這個(gè)頂點(diǎn)相關(guān)聯(lián)的每個(gè)邊。這樣就產(chǎn)生了樹(shù)里2的上的頂點(diǎn)。遵循同樣的原則繼續(xù)下去,經(jīng)有限步驟就產(chǎn)生了生成樹(shù)。

栗子:

  用廣度優(yōu)先搜索找出圖3-9所示圖G的生成樹(shù),選擇頂點(diǎn)f作為根:

  

 

兩種著名的基本遍歷策略:

  深度優(yōu)先搜索(depth-first search)

  廣度優(yōu)先搜索(breadth-first search)

 

找出圖的連通分量:

  如果一個(gè)圖中的任何一個(gè)節(jié)點(diǎn)都有一條路徑可以到達(dá)其他各個(gè)節(jié)點(diǎn),那么它就是連通的。

  連通分量:目標(biāo)圖中最大(且獨(dú)立)的連通子圖。

  從圖中的某個(gè)部分開(kāi)始,逐步擴(kuò)大其連通子圖的確認(rèn)范圍,直至它再也無(wú)法向外連通為止。

復(fù)制代碼
def walk(G,s,S=set()):
    P,Q=dict(),set()
    P[s]=None                    # s節(jié)點(diǎn)沒(méi)有前任節(jié)點(diǎn)
    Q.add(s)                     # 從s開(kāi)始搜索
    while Q:
        u=Q.pop()
        for v in G[u].difference(P,S):   # 得到新節(jié)點(diǎn)
            Q.add(v)
            P[v]=u               # 記錄前任節(jié)點(diǎn)
    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)
復(fù)制代碼

 

深度優(yōu)先搜索:

  

  遞歸版的深度優(yōu)先搜索 :

復(fù)制代碼
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)
復(fù)制代碼

  迭代版的深度優(yōu)先搜索 :

復(fù)制代碼
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
         opkdopnojk,              # 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]
復(fù)制代碼

  通用性的圖遍歷函數(shù)

復(fù)制代碼
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))  
復(fù)制代碼

 

基于深度優(yōu)先搜索的拓?fù)渑判?/span>

  

復(fù)制代碼
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)
復(fù)制代碼

 

迭代深度的深度優(yōu)先搜索

復(fù)制代碼
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    
        opkdopnojk,        # 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]  
復(fù)制代碼

 

廣度優(yōu)先搜索

復(fù)制代碼
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  
復(fù)制代碼

 

強(qiáng)連通分量

  如果有向圖的任何一對(duì)結(jié)點(diǎn)間是相互可達(dá)的,則稱(chēng)這個(gè)有向圖是強(qiáng)連通的

復(fù)制代碼
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'}]  
復(fù)制代碼

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多