首发于微信公众号:几何思惟算法
世界上任何两个互不相识的人,最多只须要经过6个中间人,就能够创建联系。
哈佛大学的社会心理学家米尔格兰姆于1967设计了一个连锁信件实验。他将一套连锁信件随机发送给居住在内布拉斯加州奥马哈的160我的,信中放了一个波士顿股票经纪人的名字,并要求每名收信人把这封信寄给本身认为是比较接近这名股票经纪人的朋友。这位朋友收到信后,再把信寄给他认为更接近这名股票经纪人的朋友。最终,大部分信件都寄到了这名股票经纪人手中,每封信平均经手6次到达。微信
例如你认识老王,老王认识李大爷,李大爷又认识某人,如此关联,你和奥巴马之间,最多只差6我的介绍就能够加微信好友啦。设计
若是我如今知道了全部人的通信录好友,我想知道我到底能不能认识老奥,怎么验证呢?
全球有77亿人口,每一个人的好友圈也有几百上千,这样的数据量是很大的,简单的一个一个的查找是行不通的。3d
那么问题来了,人口普查哪家强,四川成都找老王。。。
全部的信息数据以下表:code
转换成图的形式会比较直观。若是把2个互相认识的人用线链接起来,问题就转化成:你和老奥之间可否找到一条通路(暂不考虑最短是否是不超过6我的)。blog
假设朋友的朋友都是朋友,朋友的敌人也是朋友(或者敌人的朋友仍是朋友,whatever...)。io
咱们把全部直接认识的,或者能间接认识的都放到一个大集合中,创建一个大朋友圈。
问题就变成:老奥在不在咱们的大朋友圈里?class
若是你的大朋友圈里面有人认识川普,那就要把川普的朋友圈里面的全部人都加进来,造成一个新的朋友圈。效率
相信敏锐的你已经发现问题的本质,这里面只有2个重要的操做,来跟我一块儿大声朗读,并...查...。这就须要一种能高效处理集合的合并与查找的算法,并查集就是专门为这种场景量身定制。二维码
并查集本质是一个森林,里面有不少树。
每一个树有一个根,以不一样的根表明不一样的集合。以下,root1,root2表明两个集合。
初始时,每一个元素都属于一个独立的集合,该元素做为根。每一个根指向一个虚拟根-n,表明权重(表示该集合有n个元素)。
更新合并
将权重小的集合的根指向权重大的集合的根(此操做是为尽可能下降树的深度)。
查找
判断2个元素是否属同一集合,只需向上查找根,再判断是否相同。
过程当中作路径压缩,加快下一次查找速度。
查找
int findFather(int s) { int root = s, temp; // 查找s的最顶层根 while (father[root] >= 0) { root = father[root]; } // 路径压缩,提升后续查找效率 while (s != root) { temp = father[s]; father[s] = root; s = temp; } return root; }
合并
void unionSet(int s, int e) { int rootS = findFather(s); int rootE = findFather(e); int weight = father[rootS] + father[rootE]; // 将结点数少的集合做为结点数多的集合的儿子节点 if (father[rootS] > father[rootE]) { father[rootS] = rootE; father[rootE] = weight; } else { father[rootE] = rootS; father[rootS] = weight; } }
例题,poj1182,poj1308,poj1456,poj1611
扫描下方二维码关注公众号,第一时间获取更新信息!