算法总结(一): 并查集

并查集

目的: 解决元素分组问题算法

用途:
一、判断有向图中是否产生环
二、维护无向图的连通性,判断两个点是否在同一个连通块中数组

操做:
一、初始化: 每一个集合的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
}

更多例题
------------恢复内容结束------------

相关文章
相关标签/搜索