笔试面试题目:周芷若和并查集

缘来缘起

大家周末好,今天来图解一种重要的数据结构:并查集。为什么要聊并查集呢?一是因为它的思路很优美,二是因为它在笔试面试中频繁出现。来看看小米公司的一道面试题目:

有 n 个人和 m 对好友关系,如果两个人是直接或间接的好友,则认为他们属于同一个朋友圈,请写程序求出这 n 个人里一共有多少个朋友圈。 

举例:n=5,m=3,3对好友关系为 {{1, 2}, {2, 3}, {4, 5}},即1和2是好友,2和3是好友,4和5是好友,则1、2、3属于同一个朋友圈,4、5 属于另一个朋友圈。那么,这5个人被划分为2个朋友圈。

思路分析

在解决这个问题之前,我们得想一种合理的数据结构,然而,貌似书本上的数据结构都不合适,那怎么办呢?

我们来讲一个周芷若与并查集的故事。先来分析一下,好友关系如下:

 {阳顶天,金毛狮王}

 {金毛狮王,张无忌}

 {张无忌,胡青牛}

 {灭绝师太,丁敏君}

 {灭绝师太,周芷若}

 {黄蓉,梅超风}

图片

从上图可知,这些人属于不同的3个朋友圈,那么这3个朋友圈是怎么得出来的呢?

很显然,我们可以在每个朋友圈定义一个名义上的leader. 

  • 如果要判断两个人是否属于一个朋友圈,只需要判断他们的leader是否为同一个人,这是一个查询的过程。

  • 如果两个人是好友关系,则需要把两个人并入同一个朋友圈,这是一个合并的过程。

这一查一并的操作,就分出了最终有多少个朋友圈,对应的数据结构就是并查集。在很多笔试面试或竞赛题中,并查集屡见不鲜。

编程实现

下面,我们用C++代码来实现并查集,并给出测试:

#include <iostream>using namespace std; #define N 1000int leader[N + 1] = {0}; // 一个充分大的数组 // 初始化void setLeader(){  int i = 1;  for(i = 1; i <= N; i++)  {    leader[i] = i; // 初始化时,将自己初始化为自己的领导  }} // 查找领导,看看究竟是谁int findLeader(int n) {  int r = n;  while(leader[r] != r)  {    r = leader[r]; // 没找到的话,一直往上找  }   return r;} // 将两个领导的朋友圈合并, 从此,leaderX和leaderY建立了新的统一战线,是一个大朋友圈了void uniteSet(int leaderX, int leaderY){  leader[leaderX] = leaderY;  // leader[leaderY] = leaderX;} // 输入数组, 每一行表示一对好友关系,比如第一行表示3和4是好友关系int input[] = {  3, 4,  4, 2,  7, 6,   5, 1,  3, 9,  11, 8,  6, 10,  9, 13,  11, 12,}; // 测试数组,测试每行的两个整数是否属于同一个朋友圈int test[] ={  3, 2,  9, 4,  7, 10,  6, 7,  13, 4,  8, 12,   6, 9,  4, 7,  11, 10,  1, 2,  12, 13,  7, 13,};  int main(){  int numberOfSets = 13; // 总共有13个元素, 即1, 2, 3, 4, ...., 13   // 初始化领导  setLeader();   int i = 0;  int j = 0;  int n = sizeof(input) / sizeof(input[0]) / 2;  for(j = 0; j < n; j++)  {    int u = input[i++];    int v = input[i++];        // 找领导    u = findLeader(u);    v = findLeader(v);     // 领导不相等,则合并两个朋友圈    if(u != v)    {      uniteSet(u, v);      numberOfSets--;    }  }   i = 0;  n = sizeof(test) / sizeof(test[0]) / 2;  for(j = 0; j < n; j++)  {    int u = test[i++];    int v = test[i++];        // 找领导    u = findLeader(u);    v = findLeader(v);     // 如果领导不相同,则不属于一个朋友圈;如果两个领导相同,则肯定属于一个朋友圈    if(u != v)    {      cout << "NO" << endl;    }    else    {      cout << "YES" << endl;    }  }   // 经合并后,最后的朋友圈是4个:  // {3, 4, 2, 9, 13}, {7, 6, 10,}, {5, 1}, {11, 8, 12}  cout << numberOfSets << endl;   return 0;}

最后的话

实际上,并查集可以进行压缩路径优化,即让leader是每一个成员的直接上级,减少查找次数。所以,上图其实可以优化为:

图片

最后,希望大家对并查集了如指掌,顺利通过笔试、面试和竞赛,拿到更好的offer. 周末愉快,下次见。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值