1. 程式人生 > >tarjan演算法各種應用

tarjan演算法各種應用

Robert Tarjan,一個很牛逼的電腦科學家。

tarjan演算法真的是一個神奇的演算法,一個簡單的dfs卻可以解決連通性的問題以及求最近公共祖先

1.求強連通分量
首先介紹一下什麼是強連通分量。

強連通(Strongly Connected)是指一個有向圖(Directed Graph)中任意兩點v1、v2間存在v1到v2的路徑(path)及v2到v1的路徑。
強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。

當我們知道了強連通分量是什麼了之後,我們來學習一下tarjan演算法是如何求強連通分量的。
其實tarjan演算法寫起來很簡單,就是一遍dfs,但是理解起來確實有點麻煩。

首先,
dfs就是我們通常的dfs,每個點需要標記有沒有訪問過,如果沒有就往下dfs。
dfs出來的其實是一個搜尋樹。

每個節點我們需要儲存兩個資訊
1.dfn[x],表示x是第幾個訪問的,就相當於有個cnt,每呼叫一層dfs,就使得cnt+1,然後賦值給當前訪問的節點。
2.low[x],這個就不太好理解了,指的是,從這個點往下繼續搜尋所能搜尋到的反回邊所指向的已經搜過的節點的最小編號。

這個low[x]其實是在dfs的過程中遞迴求出來的一個結果,對於u->v分兩種情況
(0.初始時low[u]=dfn[u],如果這個點處理完low[u]仍然不能改變的話,說明無法搜尋到已訪問過的點)
1.v是沒有訪問過的節點,dfs(v),回溯時low[u]=min(low[u],low[v]);
2.v是已經訪問過的節點,如果該點還沒有被劃分到某一個強連通分量裡,low[u]=min(low[u],dfn[v]);

第2種情況可以理解為維護low的遞迴邊界,因為找到了已訪問過的節點,否則就遞迴下去並用low[v]去更新low[u];

其次,
我們在dfs的時候還需要維護一個棧,每訪問到一個點就將其進棧,這個棧其實就是用來存在同一個強連通分量裡的點。

那麼什麼時候出棧呢?
當一個點處理完後,low[u]==dfn[u]的時候。之前說過了,low[u]==dfn[u]表明這個點無法搜尋到已經訪問過的點,那麼當前棧內,從這個點往上的所有點就都屬於這個強連通分量,並將它們出棧。

怎麼理解呢?
如果low[u]==dfn[u],那麼說明從u點往後搜尋的點沒有能搜到在u點之前訪問的點,那麼他們一定不會屬於更往前的點組成的強連通分量。有人又問了,這樣也未必屬於u所在的強連通分量啊。可是如果這個點不屬於u所在的強連通分量,也不屬於u之前訪問的點的強連通分量,那麼它一定已經和自己所在的強連通分量的所有點一起出棧了,所以棧中u往上的所有點就都是u所在的強連通分量。

程式碼實現

vector <int> a[maxn];
int index=0,cnt=0;
int low[maxn],dfn[maxn],vis[maxn],id[maxn];
stack <int> s;
void tarjan(int x)
{
    index++;
    low[x]=dfn[x]=index;
    s.push(x);
    vis[x]=1;
    int l=a[x].size();
    for(int i=0;i<l;i++)
    {
        int t=a[x][i];
        if(dfn[t]==0)
        {
            tarjan(t);
            low[x]=min(low[x],low[t]);
        }
        else
        if(id[t]!=0)
            low[x]=min(low[x],dfn[t]);
    }
    if(low[x]==dfn[x])
    {
        cnt++;
        while(!s.empty())
        {
            int t=s.top();
            s.pop();
            id[t]=cnt;
            if(t==x)
                break;
        }
    }
}

2.tarjan求割點
理解了dfn和low的意思之後,tarjan求割點就很好理解了。
先來看割點的定義:

在一個無向圖中,如果有一個頂點集合,刪除這個頂點集合以及這個集合中所有頂點相關聯的邊以後,圖的連通分量增多,就稱這個點集為割點集合。
如果某個割點集合只含有一個頂點X(也即{X}是一個割點集合),那麼X稱為一個割點。

換句話說就是在一個無向連通圖中,如果某一個點被去掉之後使得剩下的圖不再連通,那麼這個點就是割點,它把圖分割了。

求割點就是在上面所展示的過程中,找到某個點u,如果dfn[u]<=low[u],那麼這個節點u就是割點。
為什麼?
dfn[u]<=low[u]表示u點往下搜尋無法搜尋到更往前訪問過的點。
如果無法搜尋到之前的點,那麼就表示把這個點拿掉,一個連通圖就被分成了兩個或多個連通圖。
我們可以通過下圖直觀地感受一下。
如圖所示是割點最基本的表現形式。
這裡寫圖片描述
可以看出
圖中紅色部分與藍色部分與頂點t組成了一個連通圖,而兩部分要想到達彼此,必須經過t點。所以當我們把t點去掉時,一個連通圖就變成了兩個,即紅色部分與藍色部分。
所以t點就是一個割點。
我們會發現,無論我們搜尋順序是 紅->t->藍 還是 藍->t->紅,我們都無法從t點往後搜尋到t點往前的點,因為必然要兩次經過t點,而一個點我們只走一次,所以dfn[u]<=low[u].

3.tarjan求橋

tarjan求橋和求割點是差不多的,在dfs的時候,對於任意一條邊u->v,如果dfn[u]

相關推薦

tarjan演算法各種應用

Robert Tarjan,一個很牛逼的電腦科學家。 tarjan演算法真的是一個神奇的演算法,一個簡單的dfs卻可以解決連通性的問題以及求最近公共祖先。 1.求強連通分量 首先介紹一下什麼是強連通分量。 強連通(Strongly Connecte

圖論初步-Tarjan演算法及其應用

暑假刷了一堆Tarjan題到頭來還是忘得差不多。 這篇部落格權當複習吧。 一些定義 無向圖 割頂與橋 (劃重點) 圖G是連通圖,刪除一個點表示刪除此點以及所有與其相連的邊。 若刪除某點u後G不再連通,那麼u是G的一個割頂(割點)。 若刪除某邊e後G不再連通,那麼e是G的一個橋。 雙連通 一個圖為雙

Tarjan演算法三大應用之強連通分量

Tarjan是一個對圖的分析的強有力的演算法,主要應用有:有向圖的強連通分量、無向圖的割點橋與雙連通分量、LCA(最近公共祖先) 基本概念 下面主要介紹tarjan演算法在強連通分量中的應用。 首先我們需要知道強連通是有向圖特有的概念,如果一個有向圖中

Tarjan演算法三大應用之雙連通分量

基本概念 定義1: 割點集合:點集V′∈V,若G刪除了V′後不連通,但刪除了V′的任意真子集後G仍然連通,則稱V′為割點集合 割點:若某一結點就構成了割點集合,那麼稱此結點為割點或

KMP演算法詳解及各種應用

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

hihoCoder1183 tarjan演算法應用之割邊和割點

#include<cstdio> #include<vector> #include<algorithm> using namespace std; int n,m,order=0; int low[20004],dfn[20004],father[20004],

雙指標演算法以及各種應用

這裡雙指標指的是在同一個可迭代物件a中使用兩個迭代器(下面用i,j)。 反正就要能類似陣列這樣使用索引i, j來得到對應的值a[i], a[j],然後通過移動i跟j達到目的。下面以陣列為例 常見的使用方法有幾種: 1. i跟j在陣列的兩邊開始,用來把陣列分成兩部分 這個我們早遇到

windows的各種應用

張兆森 標簽:張兆森1、windows的七層模型從低到高有:物理層、數據鏈路層、網路層、傳輸層、會話層、表示層、應用層 網絡中的應用1、ARP協議的作用:將ip地址解析成mac地址ARP欺騙:是截獲信息ARP攻擊:是讓網絡無法上網2、是通過目標ip地址,查詢目標設備的mac地址,

數組的各種應用

tin 聲明 tint alloc ear reat () 應用 byte public class Test03 { /** * @param args */ public static void main(String[] args) {

http轉https的各種應用

tar .com com htm tps targe content target lan http://www.lccee.com/content-57.html https://www.gworg.com/ssl/127.htmlhttp轉https的各種應用

山東大學軟體學院 《資料結構、演算法應用 c++語言描述》實驗指導書

實驗要求 採用良好的程式設計風格;關鍵操作要有註釋。 程式能夠執行,顯示執行結果。 二、      開發工具        

KMP演算法與其應用

KMP字串匹配 題目連結:https://www.luogu.org/problemnew/show/P3375 1.nxt陣列: nxt[x]:以x位結尾的字串為字尾能匹配到的最長字首。 求法見程式碼: nxt[1]=0;int j=0; for(int i=2;i<

TARJAN演算法與其運用

一.割點與割邊 題目連結 http://hihocoder.com/problemset/problem/1183 割點: x不是根:只要有low[v[x]]>=low[x] 說明v[x]不通過x沒法回到x來時的地方,所以x為割點 x是根:x有兩個及以上的直接兒子(注意

css的各種應用

6.1列表: 1.無序列表:語法 無序列表的特性:沒有順序,每個 標籤獨佔一行(塊元素) 預設 標籤項前面有個實心小圓點 一般用無序型別的列表,如導航·側邊新聞·有規律的圖文組合模組等 2.有序列表:語法 有

tarjan演算法入門(三)——有向圖的強連通分量

一.概述. 強連通分量SCC是基於有向圖的一個概念,即“極大連通分量”.有向圖的強連通分量就是說一張圖G的子圖G',G'的每一個點u都可以遍歷到這張圖上的任意一個點v,且這張子圖G'極大,極大的意思可以參考雙連通分量的極大.   二.強連通分量與tarjan演算法. t

tarjan演算法入門(二)——無向圖的雙連通分量(未完成)

一.雙連通分量. 無向圖的雙連通分量分為兩種,一種是點雙連通分量,簡稱v-DCC;一種是邊雙連通分量,簡稱e-DCC. 一個v-DCC的概念即為一張沒有割點的圖,一個e-DCC的概念即為一張沒有割邊的圖. 一張極大雙連通分量子圖的是指沒有一張雙連通分量子圖完全包含這張子圖的所有點且點

演算法--貪心演算法應用

一、思想 貪心演算法在每一步做出當時看起來最佳的選擇,也就是說總是做出區域性最優的選擇,希望這樣能得到全域性最優解,貪心演算法不一定能得到最優解,產生最優解的條件是: 1.最優子結構; 2.貪心選擇性:當一個問題的全域性解可以通過區域性最優解得到,就稱這個問題具有貪心選擇性。 證明思路:假定首選元素不

資料結構 筆記:KMP演算法應用

成員函式 功能描述 indexOf(s) 查詢子串s在字串中的位置 remove(s) 將字串中的子串s刪除 operator-(s) 定義字串減法

MTCNN演算法提速應用(ARM測試結果評估) MTCNN演算法提速應用(ARM測試結果評估)

原 MTCNN演算法提速應用(ARM測試結果評估) 置頂 2017年11月02日 10:48:05 samylee 閱讀數:11584

貪心演算法典型應用之——以最小前進次數到達陣列最後一個位置

1、題目說明: 輸入一個所有元素都是自然數的陣列,初始狀態你的位置位於第1個元素,每個元素的位置表示1步,當前所在位置的元素數值表示你下一次前進能夠移動的最大步數,你的目標是以最小的前進次數從陣列的第一個元素移動到陣列的最後一個元素位置,你需要輸出每次前進的步數。 2、舉例: