好玩算法:只需通过6人介绍,就可认识世界任何一个人!

点击上方蓝字 关注我,涨知识

01

PART

六度分隔理论

世界上任何两个互不相识的人,最多只需要通过6个中间人,就可以建立联系。

哈佛大学的社会心理学家米尔格兰姆于1967设计了一个连锁信件实验。他将一套连锁信件随机发送给居住在内布拉斯加州奥马哈的160个人,信中放了一个波士顿股票经纪人的名字,并要求每名收信人把这封信寄给自己认为是比较接近这名股票经纪人的朋友。这位朋友收到信后,再把信寄给他认为更接近这名股票经纪人的朋友。最终,大部分信件都寄到了这名股票经纪人手中,每封信平均经手6次到达。

例如你认识老王,老王认识李大爷,李大爷又认识某人,如此关联,你和奥巴马之间,最多只差6个人介绍就可以加微信好友啦。

02

PART

引发思考

如果我现在知道了所有人的通讯录好友,我想知道我到底能不能认识老奥,怎么验证呢?
全球有77亿人口,每个人的好友圈也有几百上千,这样的数据量是很大的,简单的一个一个的查找是行不通的。

那么问题来了,人口普查哪家强,四川成都找老王。。。
所有的信息数据如下表:

转换成图的形式会比较直观。如果把2个互相认识的人用线连接起来,问题就转化成:你和老奥之间能否找到一条通路(暂不考虑最短是不是不超过6个人)。

03

PART

问题建模

假设朋友的朋友都是朋友,朋友的敌人也是朋友(或者敌人的朋友还是朋友,whatever...)。

我们把所有直接认识的,或者能间接认识的都放到一个大集合中,建立一个大朋友圈。
问题就变成:老奥在不在我们的大朋友圈里?

如果你的大朋友圈里面有人认识川普,那就要把川普的朋友圈里面的所有人都加进来,形成一个新的朋友圈。

相信敏锐的你已经发现问题的本质,这里面只有2个重要的操作,来跟我一起大声朗读,并...查...。这就需要一种能高效处理集合的合并与查找的算法,并查集就是专门为这种场景量身定制。

04

PART

算法理论

并查集本质是一个森林,里面有很多树。

每个树有一个根,以不同的根代表不同的集合。如下,root1,root2代表两个集合。

如初始时,每个元素都属于一个独立的集合,该元素作为根。每个根指向一个虚拟根-n,代表权重(表示该集合有n个元素)。

更新合并
将权重小的集合的根指向权重大的集合的根(此操作是为尽量降低树的深度)。

查找

判断2个元素是否属同一集合,只需向上查找根,再判断是否相同。
过程中做路径压缩,加快下一次查找速度。

05

PART

代码实现

5.1

查找

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;
}

5.2

合并

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

关注我,涨知识

科学眼光看世界,

传播知识传播爱!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值