1. 程式人生 > >c2java 第7篇 圖的連通分量,關節點和橋

c2java 第7篇 圖的連通分量,關節點和橋

圖的連通分量,關節點和橋
====
對於有向圖,我們稱其一個子圖是強連通分量,是指任意兩點u,v, 都有兩條路徑u到v和v到u。

對於連通無向圖,我門稱其一個子圖是雙連通分量,是指任意兩點u,v,存在一個圈包含u,v。與無向圖相關聯的還有關節點x,是指去掉x,圖不連通;橋(u,v)是指去掉這條邊,圖不連通。

求解演算法的要義在於首先要理解:
樹邊-前向邊-後向邊-交叉邊
"Consider what happens when a depth-first search is performed on a directed
graph G. The set of edges which lead to a new vertex when traversed during the
search form a tree. The other edges fall into three classes. Some are edges running from ancestors to descendants in the tree. These edges may be ignored, because
they do not affect the strongly connected components of G. Some edges run from
descendants to ancestors in the tree; these we may call fronds as above. Other edges run from one subtree to another in the tree. These, we call cross-links."[1] 
每個強連通分量為搜尋樹中的一棵子樹。

定義:
 o dfn[u]為節點u搜尋的次序編號(時間戳).
 o low[u]為 u所在強連通子圖對應的搜尋子樹的根節點的dfn值(u或u的子樹能夠追溯到的最早的棧中節點的次序號).
 o 為了處理有向圖的交叉邊,引入comp[u]表示u屬於於哪個連通分量.

對於邊(x,y):
o (dfn[y] < dfn[x]) 表示這條邊是後向邊或者交叉邊。
o (0 == comp[y])表示還在棧裡面,即這條邊不可能是交叉邊。
o (low[y] >= dfn[x]) 表明x搜尋樹子樹的根,即無向圖的關節點 ([1] LEMMA5)。
o (low[y] > dfn[x]) 表明這條邊是無向圖的橋。

討論:
o 對於無向圖討論強連通沒有意義,因為它總是強連通的;對於有向圖討論關節點和橋,結果全是亂的。

o 求得scc之後,把每個scc縮成一個點,加上原圖的邊,構成一個有向無環圖dag。
我可以邊dfs邊構造dag:在 comp[y]不為零時記錄dag的邊, 在輸出scc時增加一個頂點,並添上剛才記錄的邊。與此有關的一個專利[3], 要是Tarjan先生把他的dfs框架專利了,還容得爾曹唧唧歪歪.

o Q:把一個有向圖變成強連通,至少需要加多少條邊?
  A: 在dag中統計入度為零的頂點數s1和出度為零的頂點數s2, 結果為max(s1,s2)。
  證明:不妨設s1>s2, 兩組頂點編號為I[1..s1], D[1..s2], 可以這樣連:D[1] 連I[2], D[2] 連I[3], ..., D[s2] 連I[s2+1],然後
  I[s2+1] 連I[s2+2],I[s2+2]連I[s2+3],..., I[s1]連I[1]構成一個大外環。

o Q:把一個無向圖變成雙連通,至少需要加多少條邊?
  A:bcc 縮點後形成一棵樹,統計樹的葉子數目為s,則結果為(s+1)/2。
  證明[4]:最近公共祖先(LCA)最遠的兩個葉子連一條邊,然後再找下一個LCA最遠的兩個葉子連一條邊,..., 這樣兩兩配對。
  為什麼要跟LCA扯上關係,因為這樣才能兩個半圈不會部分重複。

o 強連通分量的應用:
Q: POJ2762 對於一個很大的有向圖的任意兩點,是否可達,要求複雜度為O(N)。
A: scc + 縮點 + dfs。
Q: IOPC2006 給定一個有向圖G(V,E),問最多能從G圖中刪去多少條邊,且刪了邊之後圖G的連通性不變。換一種描述:給定一個有向圖G(V,E),我們可以改造這個圖G中的連邊得到新圖G’,問圖G’中至少要含有多少邊,才能滿足G’的連通性與圖G一致。
A: scc + 縮點後,按逆拓撲順序,加入u和u出發的邊,檢查這些新加入的邊是否可刪除:
對於<u,v>, 如果存在x,使得u,x有邊,且x到v有路徑,則<u,v>可刪。[2]
 
/*

history: 
2014.04.25  StringBuffer out 改成  LinkedList<Integer> out
2014.04.29  add Tarjan 
*/
import java.io.*;
import java.util.*;

public class Graph
{
	int n; /*頂點數*/
	int[][] adjList;
	int[] edgeCnt, edgeCap;
	
	void trimToSize()
	{
		int i;
		for(i = 0; i < n; ++i){
			adjList[i] = Arrays.copyOf(adjList[i], edgeCnt[i]);
		}

		edgeCnt = null; edgeCap = null; 
	}
	
	int addEdge(int v, int w)
	{
		if(edgeCnt[v] >= edgeCap[v]){
			int newCap = edgeCap[v] + 10;

			adjList[v] = Arrays.copyOf(adjList[v], newCap);
			edgeCap[v] = newCap;
		}
		adjList[v][edgeCnt[v]++] = w;

		return 0;
	}

	int[] getNeighbors(int v)
	{
		return adjList[v];
	}

	void dfs(int v, byte[] color, LinkedList<Integer> out)
	{
		int i, w = 0;
		int[] nei = getNeighbors(v);
		
		color[v] = 1;
		for(i = 0; i < nei.length; ++i){
			w = nei[i];
			if(0 == color[w])dfs(w, color, out);
			else if(1 == color[w]){/*a cycle is found.*/}
		}
		color[w] = 2;
		out.addFirst( v);
		//System.out.printf("v = %d out = %s %n", v, out);
	}

	void topoSort(byte[] color, LinkedList<Integer> out )
	{
		int i;
		out.clear();
		Arrays.fill(color, (byte)0);
		for(i = 0; i < n; ++i){
			if(0 == color[i])dfs(i, color, out);
		}
	}

	void bfs(int v, byte[] color, LinkedList<Integer> out)
	{
		int i, w;
		int[] nei;
		LinkedList<Integer> qu = new LinkedList<Integer>();
	
		out.clear();
		Arrays.fill(color, (byte)0);
		
		qu.push(v);
		while(!qu.isEmpty()){
			v = qu.pop();
			out.addLast(v);
			color[v] = 1; /*visited*/
			
			nei = getNeighbors(v);
			for(i = 0; i < nei.length; ++i){
				w = nei[i];
				if(0 == color[w])qu.push(w);
			}
		}
	}
	
	public Graph(int vertexNum, int edgeNum, Scanner in) throws IOException
	{/*假設輸入頂點數,邊數,之後是m對邊(v,w)*/
		
		int i, v, w;

		n = vertexNum;
		adjList = new int[n][0];
		edgeCnt = new int[n];
		edgeCap = new int[n];
		for(i = 0; i < n; ++i){
			edgeCnt[i] = 0;
			edgeCap[i] = 10;
			adjList[i] = new int[edgeCap[i]];
		}

		for(i = 0; i < edgeNum; ++i){
			v = in.nextInt(); // - 1;
			w = in.nextInt(); // - 1;
			addEdge(v, w);
		}
		trimToSize();
	}

}

class Tarjan
{
	Graph g;
	int n;
	
	int[] dfn; /*每個頂點的dfn值*/
	int[] low;/*每個頂點的low值*/
	int[] stk;/*儲存分量頂點的棧*/
	int stp;
	int	index; /*搜尋次序*/

	int[] comp; /*標記每個頂點屬於哪個連通分量*/
	int compTag;

	int[] cut;/*標記每個頂點是否為割點*/
	int son; /*根節點孩子個數,大於2則根節點為關節點*/ 
	int beginVertex;

	public Tarjan(Graph gRef)
	{/*common context init*/
		g = gRef;
		n = g.n;
		dfn = new int[n]; 
		low = new int[n]; 
	}
	
	public void SCC()
	{/*求有向圖的強連通分量*/
		int i;
		Arrays.fill(dfn, 0);
		Arrays.fill(low, 0);	
		stk = new int[n]; stp = 0; 
		comp = new int[n]; compTag = 0;
		index = 0;
	
		
		i = 0; //2;
		for(; i < n; ++i){
			if(0 == dfn[i])strongConnect(i);
		}
		stk = null;
		comp = null;
	}

	void strongConnect(int x)
	{
		int[] adj;
		int i, y;
		
		stk[stp++] = x;	
		dfn[x] = low[x] = ++index;
		adj = g.getNeighbors(x);
		for(i = 0; i < adj.length; ++i){
			y = adj[i];
			if(0 == dfn[y]){
				strongConnect(y);
				low[x] = Math.min(low[x], low[y]);
			}else if((dfn[y] < dfn[x]) && (0 == comp[y])){
				low[x] = Math.min(low[x], dfn[y]);
			}	
		}

		if(low[x] == dfn[x]){
			++compTag;
			System.out.printf("SCC(%d): ", compTag);
			do{
				y = stk[--stp];
				comp[y] = compTag;
				System.out.printf("%d:(%d,%d), ", y, dfn[y], low[y]);
			}while(y != x);
			System.out.printf("%n");
		}
	}

	public void BCC()
	{/*求無向圖的雙連通分量,*/
		int i;

		Arrays.fill(dfn, 0);
		Arrays.fill(low, 0);
		stk = new int[4*n]; stp = 0; 
		cut = new int[n]; 
		
		beginVertex = 0; son = 0;
		index = 0;
		biConnect(beginVertex, -1);
		if(son > 1)cut[beginVertex] = 1;
		
		System.out.printf("Cut: ");
		for(i = 0; i < n; ++i)if(cut[i] > 0)System.out.printf("%d, ", i);
		System.out.printf("%n");

		stk = null;
		cut = null;
	}

	void biConnect(int x, int father)
	{
		int i, y, x1, y1;
		int[] nei;

		dfn[x] = low[x] = ++index;
		nei = g.getNeighbors(x);
		for(i = 0; i < nei.length; ++i){
			y = nei[i];
			if(y == father)continue;

			if(0 == dfn[y]){
				stk[stp++] = x; stk[stp++] = y;
				biConnect(y,x);	
				low[x] = Math.min(low[x], low[y]);

				if(low[y] >= dfn[x]){
					System.out.printf("BCC(%d,%d): ", x, y);
					do{
						y1 = stk[--stp]; x1 = stk[--stp];
						System.out.printf("<%d:(%d,%d),%d:(%d,%d)>, ", x1, dfn[x1], low[x1], y1, dfn[y1], low[y1]);
					}while(dfn[x1] >= dfn[y]);

					System.out.printf("%n");
				}

				if(x == beginVertex)++son;
				if(x != beginVertex && low[y] >= dfn[x])cut[x] = 1;
				
				if(low[y] > dfn[x])System.out.printf("Bridge <%d %d> %n", x, y);
					
			}else if(dfn[y] < dfn[x]){
				stk[stp++] = x; stk[stp++] = y;
				low[x] = Math.min(low[x], dfn[y]);
			}	
		}
	}

}

class Test
{
	public static void main (String[] arg) throws IOException
	{
		int n, m;
		byte[] color; /*for dfs: 0--white, 1--gray, 2--black*/
		LinkedList<Integer> out = new LinkedList<Integer>();

		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		color = new byte[n];

		Graph g = new Graph(n, m,  in);
		in.close();
		in = null;

		Arrays.fill(color, (byte)0);
		g.dfs(0, color, out);
		System.out.println("dfs: " + out);
		
		g.bfs(0, color, out);
		System.out.println("bfs: " + out);

		g.topoSort(color, out);
		System.out.println("topoSort: " + out);
		color = null;
		out = null;

		Tarjan t = new Tarjan(g);
		t.SCC();
		t.BCC();

	}
}

對於這個圖的輸出結果:


[email protected] ~/java
$ cat in.txt
8 14
0 1
1 2 1 4 1 5
2 3 2 6
3 2 3 7
4 0 4 5
5 6
6 5 6 7
7 7

[email protected] ~/java
$ javac -encoding UTF-8 Graph.java  && cat in.txt | java Test
dfs: [0, 1, 4, 2, 6, 5, 3, 7]
bfs: [0, 1, 5, 6, 7, 4, 2, 3]
topoSort: [0, 1, 4, 2, 6, 5, 3, 7]
SCC(1): 7:(5,5),
SCC(2): 5:(7,6), 6:(6,6),
SCC(3): 3:(4,3), 2:(3,3),
SCC(4): 4:(8,1), 1:(2,1), 0:(1,1),
BCC(3,7): <3:(4,4),7:(5,5)>,
Bridge <3 7>
BCC(2,3): <2:(3,3),3:(4,4)>,
Bridge <2 3>
BCC(6,5): <6:(6,6),5:(7,7)>,
Bridge <6 5>
BCC(2,6): <6:(6,5),7:(5,5)>, <2:(3,3),6:(6,5)>,
Bridge <2 6>
BCC(1,2): <1:(2,2),2:(3,3)>,
Bridge <1 2>
BCC(0,1): <4:(8,1),5:(7,7)>, <4:(8,1),0:(1,1)>, <1:(2,1),4:(8,1)>, <0:(1,1),1:(2,1)>,
Cut: 1, 2, 3, 6,

對這個無向圖的輸出結果:


5 10
0 1 0 4
1 0 1 2 
2 1 2 3 2 4 
3 2 
4 0 4 2

$ javac -encoding UTF-8 Graph.java  && cat in.txt | java Test
dfs: [0, 1, 2, 4, 3]
bfs: [0, 4, 2, 3, 1, 1]
topoSort: [0, 1, 2, 4, 3]
SCC(1): 4:(5,1), 3:(4,3), 2:(3,1), 1:(2,1), 0:(1,1),
BCC(2,3): <2:(3,3),3:(4,4)>,
Bridge <2 3>
BCC(0,1): <4:(5,1),0:(1,1)>, <2:(3,1),4:(5,1)>, <1:(2,1),2:(3,1)>, <0:(1,1),1:(2,1)>,
Cut: 2,

 
[1] R. Tarjan Depth-First Search and Linear Graph Algorithms.
[2] 淺談強連通分量與拓撲排序的應用 http://3y.uu456.com/bp-01a8sfefsef7ba0d4a733b3e-1.html.
[3] 標識強連通分量的入口和出口的技術 http://www.google.com/patents/CN102279738A?cl=zh
[4] 點連通度與邊連通度 https://www.byvoid.com/blog/biconnect/

相關推薦

c2java 7 連通分量關節點

圖的連通分量,關節點和橋 ==== 對於有向圖,我們稱其一個子圖是強連通分量,是指任意兩點u,v, 都有兩條路徑u到v和v到u。對於連通無向圖,我門稱其一個子圖是雙連通分量,是指任意兩點u,v,存在一個圈包含u,v。與無向圖相關聯的還有關節點x,是指去掉x,圖不連通;橋(u

R學習筆記 :函數分支循環

匿名 操作數 play 控制 als layers null 操作 str 變量用於臨時存儲數據,而函數用於操作數據,實現代碼的重復使用。在R中,函數只是另一種數據類型的變量,可以被分配,操作,甚至把函數作為參數傳遞給其他函數。分支控制和循環控制,和通用編程語言的風格很相似

DFS的運用(二分判定、無向的割頂連通分量有向的強連通分量

part str stack void div prev this 沒有 2-sat 一、dfs框架: 1 vector<int>G[maxn]; //存圖 2 int vis[maxn]; //節點訪問標記 3 void dfs(int u

Android學習7——碎片實踐結合ListView的簡單閱讀應用自適應手機平板

在學過了碎片(Fragment)、ListView之後,實現一個自適應手機和平板的文章閱讀應用效果圖:手機: 平板:二、實現過程:1、新建一個文章實體類Newspublic class News { private String title; private

Mysql高手系列 - 7:玩轉select條件查詢避免踩坑

這是Mysql系列第7篇。 環境:mysql5.7.25,cmd命令中進行演示。 電商中:我們想檢視某個使用者所有的訂單,或者想檢視某個使用者在某個時間段內所有的訂單,此時我們需要對訂單表資料進行篩選,按照使用者、時間進行過濾,得到我們期望的結果。 此時我們需要使用條件查詢來對指定表進行操作,我們需要了解sq

測開之函式進階· 7《裝飾器裝飾類通用裝飾器有啥區別呢?》

### 堅持原創輸出,點選藍字關注我吧 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20210105123255.png) 作者:清菡 部落格:oschina、雲+社群、知乎等各大平臺都有。 > 由於微信公眾號推送改為了資訊流

POJ1236 Network of Schools (強連通分量註意邊界)

註意 receive sizeof date() pos describes nim distrib uci A number of schools are connected to a computer network. Agreements have been deve

POJ 2186 - Popular Cows - 強連通分量縮點

描述 res entry algo nbsp include truct 簡便 mem 題目大意: 給定一個含N個點、M條邊的有向圖,求其中有多少個點,可以由其他任意一點出發到達它? N<=1e4,M<=5e4。 為了描述和編程簡便,我們建立原圖的反圖,這樣

小白學習server-----虛擬(目錄/主機)防火墻文檔加密

server虛擬目錄? 通過別名方式掛載到網站根目錄下的其他目錄? 虛擬目錄優點– 便於分別開發與維護– 移動位置不影響站點邏輯結構虛擬主機? 服務器上運行的多個網站稱為虛擬主機? 實現虛擬主機的方式– 使用不同IP地址– 相同IP地址,不同端口號– 相同IP地址及端口號,

開啟運維之路之 7 ——RedisDesktopManager使用、Keys通用操作、Java程式碼操作基本的Redis

RedisDesktopManager下載地址:Redis桌面管理工具官方下載地址 安裝好,直接雙擊開啟。 說明:我本機的 IP 由於使用公司的 IP ,經常會變動,但不影響連線 Linux 虛擬機器。 現在發現個問題,無法連線到 Redis 。 解決過程: ①Redi

Tarjan相關(強連通分量割點縮點)

一、先上模板QWQ #include<bits/stdc++.h>//tarjan-強連通分量 using namespace std; const int MAXM=100005; const int MAXN=5005; int n,m,tot,head[MAXN],dfn[MAXN],l

洛谷 P1262|P2341|P2002 強連通分量縮點

圖論強連通分量演算法,個人感覺tarjan相比兩次dfs好寫一點(個人看法) 這三道題都在學了強連通分量演算法之後都比較基礎,貌似都要判斷一下縮點之後每個點的入度?P1262 間諜網路 題意: 直接複製一下資料的輸入格式這裡,還是比較好理解的吧 第一行只有一個整數n。 第二行是整數p。表示願

___求無向連通分量個數

求無向圖連通分量個數方法:    基於DFS,從某一頂點出發遍歷圖,for迴圈,改變起始頂點,count計數。 程式碼如下: void DFSTraverse(ALGraph G){ //深度遍歷圖 void DFS(ALGraph G,

7 JDK5.0新特性

Jdk5.0新特性: Collection在jdk1.5以後,有了一個父介面Iterable,這個介面的出現的將iterator方法進行抽取,提高了擴充套件性。 -------------------------------------------------- 增強for迴圈:fore

redis 7 Redis資料型別----Set

集合型別 集合型別:無序、不可重複 列表型別:有序、可重複   命令 增加/刪除元素   語法:SADD key member [member ...] 127.0.0.1:6379> sadd set a b c

讀書筆記 ---- 《深入理解Java虛擬機器》---- 7:虛擬機器位元組碼執行引擎

上一篇:虛擬機器類載入機制:https://blog.csdn.net/pcwl1206/article/details/84260914 第7篇:虛擬機器位元組碼執行引擎 執行引擎是Java虛擬機器最核心的組成部分之一。“虛擬機器”是一個相對於“物理機”的概念,這兩種機器都有程式碼執行能力

HTML5學習7—自定義屬性data-*

html5中的自定義屬性,以data-開頭,可以通過html5提供的api獲取 示例1: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>

Shiro安全框架| Shiro的認證授權

  Shiro的認證 接下來是在IDEA進行Shiro的學習,新建一個Springboot工程,除了一些預設的依賴,這裡需要引入apache.shiro安全框架依賴,以及做單元測試的依賴。 1<dependencies> 2   &

大話資料結構讀書筆記艾提拉總結 查詢演算法 排序演算法比較好 1章資料結構緒論 1 2章演算法 17 3章線性表 41 4章棧與佇列 87 5章串 123 6章樹 149 7 21

大話資料結構讀書筆記艾提拉總結   查詢演算法 和排序演算法比較好     第1章資料結構緒論 1 第2章演算法 17 第3章線性表 41 第4章棧與佇列 87 第5章串 123 第6章樹 149 第7章圖 211

【搞定Java併發程式設計】7:Java記憶體模型詳解

上一篇:ThreadLocal詳解:https://blog.csdn.net/pcwl1206/article/details/84859661 其實在Java虛擬機器的學習中,我們或多或少都已經接觸過了有關Java記憶體模型的相關概念(點選檢視),只不過在Java虛擬機器中講的不夠詳細,因此