最近在抽空看看coursera那个普林斯顿的算法课,有点意思,讲的很清楚。
第一讲是关于并查集的,描述了如何快速判断若干个点组成的集合中,某对点是否连接。
算法没有什么难度,但是存储父节点这个思想很值得学习。之后的再优化就很出色了,记录一下。
以下内容我记录在了自己的note里面。文字内容大概是:
union find:
问题:判断若干个点组成的集合中,某对点是否连接。
判断是否连接:
连接的等价:
reflexive,p和p本身是相连的;
symmetric,p和q相连则q和p相连;
transitive,p和q相连,q和r相连,则p和r相连。
connected components:
最大的组成集,集合内的每一个都是相连的而集合间互不相连。如此,判断p和q是否相连变成了判断p和q是否在同一个集合里。
而union(a,b)这个命令就是将有这a和b两个点的集合相合并。
创建数据集:
根据object的总数N设定一个类,将每两个点之间的相连关系输入其中,判断,如果两个点是不是在同一个set中,如果在,则跳过,否则将有这两个object的集合union,最终形成一个完整的类。
Quick find:
用索引array表示数据所属的类别,find这个行为只用查这个array的值是否相同。
union这个方法需要将一个set的类别值全都改为另一个set的值。
first:
将0~9的值赋予0~9的index。
merge时间复杂度过高,N^2
Quick union:
提高速度的方法。这个方法不是储存该id的类别,而是存该类别所在的树形结构中的父节点id。
这样查找两个点是否在同一set中的时候,只要判断是否能走向相同的root就可以,而merge的时候,只用做一次变换,将原有的根节点连接到另一个根节点上。
union的过程,查找一个id的parentid,如果不是自己(代表不是根节点),接着向上查,找到根节点再将根节点的parentid改成merge的另一个节点的root即可。
这种的find时间复杂度高N,而且如果树太高,容易造成计算root的速度太慢的问题。
Quick union改进方法
with weight
记录树的长度,在merge的时候比较树的size,让小的树改变parent id为大的树的root
左树比右树大
这样find的速度和树的深度成正比,树的深度复杂度为lgN(lg=log2)
2. with path compression
每次求root的时候,将每个子节点的父节点指向上一级节点。这样,复杂度可以提到线性的
union find的应用:
MATLAB中的bwlabel()算法
物理中的percolation,top到bottom可以用概率为p出现的白块连接
价值点:
判断top和bottom是否有相连接的点,与其遍历每一种可能,可以选择将top的所有的点连接到一个父节点,bottom连接到一个父节点,判断这两个父节点是否相连。
bear in mind, scientific thinking
python的demo:
#coding:UTF-8
'''
Created on 2014年7月21日
@author: hao
'''
import time
class quickFind():
'''
array中的值是所属的类别
'''
def __init__(self, dimension = 10):
self.idList = range(dimension)
def find(self, aPoint, bPoint):
if self.idList[aPoint]==self.idList[bPoint]:
return 'connected'
else:
return 'isolated'
def union(self, aPoint, bPoint):
if self.idList[aPoint]!=self.idList[bPoint]:
preIndex = self.idList[bPoint]
for i in range(len(self.idList)):
if self.idList[i]==preIndex:
self.idList[i] = self.idList[aPoint]
class quickUnion():
'''
array 记录 节点id
'''
def __init__(self, dimension = 10):
self.idList = range(dimension)
def find(self, aPoint, bPoint):
aRoot = aPoint
while(True):
if aRoot == self.idList[aRoot]:
break
aRoot = self.idList[aRoot]
bRoot = bPoint
while(True):
if bRoot == self.idList[bRoot]:
break
bRoot = self.idList[bRoot]
if aRoot == bRoot:
return 'connected'
else:
return 'isolated'
def union(self, aPoint, bPoint):
aRoot = aPoint
while(True):
if aRoot == self.idList[aRoot]:
break
aRoot = self.idList[aRoot]
bRoot = bPoint
while(True):
if bRoot == self.idList[bRoot]:
break
bRoot = self.idList[bRoot]
if bRoot != aRoot:
self.idList[bRoot] = aRoot
class quickUnionwithWeight():
'''
compare the depth of the tree
'''
def __init__(self, dimension = 10):
self.idList = range(dimension)
def find(self, aPoint, bPoint):
aRoot = aPoint
while(True):
if aRoot == self.idList[aRoot]:
break
aRoot = self.idList[aRoot]
bRoot = bPoint
while(True):
if bRoot == self.idList[bRoot]:
break
bRoot = self.idList[bRoot]
if aRoot == bRoot:
return 'connected'
else:
return 'isolated'
def union(self, aPoint, bPoint):
aRoot = aPoint
aDepth = 0
while(True):
if aRoot == self.idList[aRoot]:
break
aRoot = self.idList[aRoot]
aDepth+=1
bRoot = bPoint
bDepth = 0
while(True):
if bRoot == self.idList[bRoot]:
break
bRoot = self.idList[bRoot]
bDepth+=1
if bRoot != aRoot:
if bDepth>=aDepth:
self.idList[aRoot] = bRoot
else:
self.idList[bRoot] = aRoot
class quickUnionwithWeightwithPathCompression():
'''
compare the depth of the tree
'''
def __init__(self, dimension = 10):
self.idList = range(dimension)
def find(self, aPoint, bPoint):
aRoot = aPoint
while(True):
if aRoot == self.idList[aRoot]:
break
aRoot = self.idList[aRoot]
bRoot = bPoint
while(True):
if bRoot == self.idList[bRoot]:
break
bRoot = self.idList[bRoot]
if aRoot == bRoot:
return 'connected'
else:
return 'isolated'
def union(self, aPoint, bPoint):
aRoot = aPoint
aPreRoot = 0
aDepth = 0
while(True):
if aRoot == self.idList[aRoot]:
break
aPreRoot = aRoot
aRoot = self.idList[aRoot]
self.idList[aPreRoot] = aRoot
aDepth+=1
bRoot = bPoint
bPreRoot = 0
bDepth = 0
while(True):
if bRoot == self.idList[bRoot]:
break
bPreRoot = bRoot
bRoot = self.idList[bRoot]
self.idList[bPreRoot] = bRoot
bDepth+=1
if bRoot != aRoot:
if bDepth>=aDepth:
self.idList[aRoot] = bRoot
else:
self.idList[bRoot] = aRoot
if __name__=='__main__':
a=time.time()
test = quickFind(100000)
test.union(1,2)
test.union(1,3)
print test.find(2,3)
print time.time()-a
a=time.time()
test = quickUnion(100000)
test.union(1,2)
test.union(1,3)
print test.find(2,3)
print time.time()-a
a=time.time()
test = quickUnionwithWeight(100000)
test.union(1,2)
test.union(1,3)
print test.find(2,3)
print time.time()-a
a=time.time()
test = quickUnionwithWeightwithPathCompression(100000)
test.union(1,2)
test.union(1,3)
print test.find(2,3)
print time.time()-a
connected
0.0421710014343
connected
0.00213694572449
connected
0.00201916694641
connected
0.00193977355957