日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法

發布時間:2025/5/22 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、背景介紹

強連通分量是有向圖中的一個子圖,在該子圖中,所有的節點都可以沿著某條路徑訪問其他節點。強連通性是一種非常重要的等價抽象,因為它滿足

  • 自反性:頂點V和它本身是強連通的
  • 對稱性:如果頂點V和頂點W是強連通的,那么頂點W和頂點V也是強連通的
  • 傳遞性:如果V和W是強連通的,W和X是強連通的,那么V和X也是強連通的

強連通性可以用來描述一系列屬性,如自然界中物種之間的捕食關系,互相捕食的物種可以看作等價的,在自然界能量傳遞中處于同一位置。

下圖中,子圖{1,2,3,4}為一個強連通分量,因為頂點1,2,3,4兩兩可達。{5},{6}也分別是兩個強連通分量。

二、Kosaraju算法描述

Kosaraju算法通過以下步驟獲得一個有向圖的強連通分量。

  • 在圖G中,計算圖G的反向圖G'的深度優先搜索的逆后序排列。反向圖是比如原圖中有V到W的鏈接,那么反向圖中就有W到V的鏈接。逆后序排列是指,在對一個圖進行深度優先遍歷時,先進行子節點的遞歸,再將本節點加入到一個棧中,最后依次出棧以獲得的序列。逆后序排列有一個重要特性,就是如果有W到V的有向鏈接,那么實際出棧時,W仍然排在V的前面
  • 在G中進行普通的深度優先搜索,但是搜索順序不是按照正統的( i = 0, i < N )去依次搜索,而是以剛剛獲得的逆后序排列的順序進行搜索。
  • 每個以這個逆后序排列中的元素開始的DFS搜索,找到的所有元素,都是同一個強聯通分量的元素。

為什么這個算法可以獲得強連通分量呢?網上的證明很少,所以下面給出我的邏輯證明。

三、Kosaraju算法證明

我們按照算法描述的步驟往下走:

  • 按照算法的結論,假設我們已經獲得了一個逆后序排列,我們從中找兩個元素,分別是V,W,W先出棧并且通過DFS找到了V。那么,V和W就是同一個聯通分量的元素。到底是不是呢?
  • 不管是不是,我們至少可以確定對于該有向圖G,W有一條鏈接通往V,我們記作W->V。那么,對于該有向圖的反向圖G',確定有鏈接V->W
  • 我們開始思考,在什么條件下,我們能夠在反向圖 G' 中獲得V...W(即W先出棧)這樣一個排列呢?要知道,我們剛剛確定了有鏈接V->W,所以逆后序排列中,應該是V排在W的前面,W...V這樣啊?
  • 所以在G'中,要么是我們之前提到的,在V->W的同時有W->V的鏈接;要么就是W和V之間沒有任何聯系,這樣V的DFS結束之后,包含W的聯通分量的DFS才開始遍歷,才能造成W比V先出棧
  • 但是我們已經知道,V和W不是毫無關系的,確定有鏈接V->W,所以第二個可能不成立,所以必然存在一個W->V的鏈接,也就是W和V是互相聯通的!
  • 證明完畢。
  • 四、算法源碼

    ?因為代碼很長,放在github上了。代碼是在Idea中編譯運行通過的,實現了一個基本的Graph數據結構,在此基礎上實現了Kosaraju類,供參考。

    源文件


    五、更加迅速的tarjan算法

    部分內容轉自https://www.byvoid.com/blog/scc-tarjan

    1.Tarjan算法

    Tarjan算法是基于對圖深度優先搜索的算法,每個強連通分量為搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時可以判斷棧頂到棧中的節點是否為一個強連通分量。

    定義DFN(u)為節點u搜索的次序編號(時間戳),Low(u)為u或u的子樹能夠追溯到的最早的棧中節點的次序號。由定義可以得出,

    Low(u)=Min {DFN(u),Low(v),(u,v)為樹枝邊,u為v的父節點DFN(v),(u,v)為指向棧中節點的后向邊(非橫叉邊) }

    當DFN(u)=Low(u)時,以u為根的搜索子樹上所有節點是一個強連通分量。

    算法偽代碼如下

    tarjan(u) {DFN[u]=Low[u]=++Index // 為節點u設定次序編號和Low初值Stack.push(u) // 將節點u壓入棧中for each (u, v) in E // 枚舉每一條邊if (v is not visted) // 如果節點v未被訪問過tarjan(v) // 繼續向下找Low[u] = min(Low[u], Low[v])else if (v in S) // 如果節點v還在棧內Low[u] = min(Low[u], DFN[v])if (DFN[u] == Low[u]) // 如果節點u是強連通分量的根 repeatv = S.pop // 將v退棧,為該強連通分量中一個頂點 print vuntil (u== v) }

    2.算法的精要之處如下:

    • 為每個加入的節點設定序號,使得后搜索到的節點的序號一定高于前面的節點
    • 那么,如果后搜索到的節點的子節點里居然有比它本身還要小的節點,則一定出現了環。有環則必定強連通
    • 那么,把該節點的標識節點Low(u)設為發現的后向節點的值DFN(V)
    • 然后遞歸程序返回該節點的上級節點u-1,上級節點判斷Low(u-1)的值,也把它指向了剛剛找到的后向節點
    • 最后,除了后向節點本身,所有環中的節點都指向該后向節點,那么,我們找到了一個強連通分量。

    3.算法流程的演示

    從節點1開始DFS,把遍歷到的節點加入棧中。搜索到節點u=6時,DFN[6]=LOW[6],找到了一個強連通分量。退棧到u=v為止,{6}為一個強連通分量。

    返回節點5,發現DFN[5]=LOW[5],退棧后{5}為一個強連通分量。

    返回節點3,繼續搜索到節點4,把4加入堆棧。發現節點4向節點1有后向邊,節點1還在棧中,所以LOW[4]=1。節點6已經出棧,(4,6)是橫叉邊,返回3,(3,4)為樹枝邊,所以LOW[3]=LOW[4]=1。

    繼續回到節點1,最后訪問節點2。訪問邊(2,4),4還在棧中,所以LOW[2]=DFN[4]=5。返回1后,發現DFN[1]=LOW[1],把棧中節點全部取出,組成一個連通分量{1,3,4,2}。

    至此,算法結束。經過該算法,求出了圖中全部的三個強連通分量{1,3,4,2},{5},{6}。

    可以發現,運行Tarjan算法的過程中,每個頂點都被訪問了一次,且只進出了一次堆棧,每條邊也只被訪問了一次,所以該算法的時間復雜度為O(N+M)。

    求有向圖的強連通分量還有一個強有力的算法,為Kosaraju算法。Kosaraju是基于對有向圖及其逆圖兩次DFS的方法,其時間復雜度也是O(N+M)。與Trajan算法相比,Kosaraju算法可能會稍微更直觀一些。但是Tarjan只用對原圖進行一次DFS,不用建立逆圖,更簡潔。在實際的測試中,Tarjan算法的運行效率也比Kosaraju算法高30%左右。此外,該Tarjan算法與求無向圖的雙連通分量(割點、橋)的Tarjan算法也有著很深的聯系。學習該Tarjan算法,也有助于深入理解求雙連通分量的Tarjan算法,兩者可以類比、組合理解。

    ?

    轉載于:https://www.cnblogs.com/bethunebtj/p/4854946.html

    總結

    以上是生活随笔為你收集整理的Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。