算法總結(一): 並查集

並查集

目的: 解決元素分組問題算法

用途:
一、判斷有向圖中是否產生環
二、維護無向圖的連通性,判斷兩個點是否在同一個連通塊中數組

操做:
一、初始化: 每一個集合的parent都是本身
二、查詢: 查詢集合的parent
三、合併: 把不相連的元素合併到同一個集合中函數

方法

一、初始化
假若有編號爲1, 2, 3, ..., n的n個元素,咱們用一個數組fa[]來存儲每一個元素的父節點(由於每一個元素有且只有一個父節點,因此這是可行的)。
一開始,咱們先將它們的父節點設爲本身。優化

var fa = make([]int,n)
for i := 0; i < n; i++ {
    fa[i] = i
}

二、查詢
咱們用遞歸的寫法實現對錶明元素的查詢:一層一層訪問父節點,直至根節點(根節點的標誌就是父節點是自己)。
要判斷兩個元素是否屬於同一個集合,只須要看它們的根節點是否相同便可。code

find = func(x int) int {
    if x == fa[x] {
        return x
    }
    return find(fa[x])
}

路徑壓縮方法遞歸

find = func(x int) int {
    if x != fa[x] {
        fa[x] = find(fa[x])
    }
    return fa[x]
}

三、合併
合併操做也是很簡單的,先找到兩個集合的表明元素,而後將前者的父節點設爲後者便可。leetcode

merge := func(i,j int) {
    fa[find(i)] = find(j)
}

按秩合併get

merge := func(i,j int) {
    xFa,yFa := find(i),find(j)
    if xFa==yFa {
        return
    }
    // x和y不在同一個集合中,合併它們
    if xFa<yFa {
        fa[xFa]=yFa
    } else if xFa > yFa {
        fa[yFa]=xFa
    } else {
        fa[yFa]=xFa
        rank[x]++
    }
}

同時使用路徑壓縮、按秩(rank)合併優化的程序每一個操做的平均時間僅爲 O(alpha (n)),
其中 alpha (n) 是 { n=f(x)=A(x,x)} 的反函數,A 是急速增長的阿克曼函數。
由於 alpha (n) 是其反函數,故 alpha (n) 在 n 十分巨大時仍是小於5。
所以,平均運行時間是一個極小的常數。
實際上,這是漸近最優算法:Fredman 和 Saks 在 1989 年解釋了 Omega (alpha (n)) 的平均時間內能夠得到任何並查集。io

例題 Leetcode547

班上有 名學生。其中有些人是朋友,有些則不是。他們的友誼具備是傳遞性。若是已知 A 是 B 的朋友,B 是 C 的朋友,那麼咱們能夠認爲 A 也是 C 的朋友。所謂的朋友圈,是指全部朋友的集合。class

給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關係。若是M[i][j] = 1,表示已知第 i 個和 j 個學生互爲朋友關係,不然爲不知道。你必須輸出全部學生中的已知的朋友圈總數。

示例 1:

輸入: 
[[1,1,0],
 [1,1,0],
 [0,0,1]]
輸出: 2 
說明:已知學生0和學生1互爲朋友,他們在一個朋友圈。
第2個學生本身在一個朋友圈。因此返回2。

示例 2:

輸入: 
[[1,1,0],
 [1,1,1],
 [0,1,1]]
輸出: 1
說明:已知學生0和學生1互爲朋友,學生1和學生2互爲朋友,因此學生0和學生2也是朋友,因此他們三個在一個朋友圈,返回1。

注意:

  1. N 在[1,200]的範圍內。
  2. 對於全部學生,有M[i][i] = 1。
  3. 若是有M[i][j] = 1,則有M[j][i] = 1。

題解:

func findCircleNum(isConnected [][]int) (ans int) {
	n := len(isConnected)
	parent := make([]int,n)
	for i := range parent {
		parent[i] = i
	}

	var find func(x int) int
	find = func(x int) int {
		if parent[x] != x {
			parent[x] = find(parent[x])
		}
		return parent[x]
	}
	merge := func(from,to int) {
		parent[find(from)] = find(to)
	}
	for i := 0; i < n; i++ {
		for j := i+1; j < n; j++ {
			if isConnected[i][j] == 1 {
				merge(i,j)
			}
		}
	}
	for i, p := range parent {
		if i == p {
			ans++
		}
	}
	return
}

更多例題
------------恢復內容結束------------

相關文章
相關標籤/搜索