并查集解决朋友圈问题

引入

最近在网上看到了这样一道面试题:
假如已知有n个人和m对好友关系(存于数组r)如果两个人是直接或间接的好友(好友的好友的好友…),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。假如:n = 5,m = 3,r = {{1 , 2} , {2 , 3} , {4 , 5}}表示有5个人,1和2是好友,2和3是好友4和5是好友,则1、2、3属于一个朋友圈4、5属于另一个朋友圈,结果为2个朋友圈。

这里有很多的解决方案,但是最高效的应该还是使用并查集来处理。

简介

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。
需要实现的操作有:合并两个集合,判断两个元素是否属于一个集合。

合并

合并操作很容易理解,找到要和合并的两个元素的根,把两个根结点连接在一起。如图所示:
并查集

判断两个元素是否在一个集合

这个操作可以转换为寻找两个元素的根结点,判断两个元素的根结点是否相同。

**本人不才,写不出特别生动的东西,这里有一篇大神的博文深入浅出的解释了各种并查集的操作。
http://blog.csdn.net/dellaserss/article/details/7724401**

解决方案

1.这里我们首先把5个人分别看成5个独立的个体,借用vector分别以这5个人的编号为下标(注意,下标从1开始),当他们相互独立时看为自成一个朋友圈,vector记为-1。
2.接着按照题目给出的1,2为朋友,2,3为朋友,4,5为朋友,把他们按照这个规则联合在一起,并把1中存的值和2中存的值相加放入根结点1,再把2的根结点下标放入2中。
例如1,2合并,则1位置存放-2,2位置存放1;
3.最后计算vector中小于0的数有多少就可以得到朋友圈的数量。

并查集

代码实现

#include<vector>
#include<iostream>
using namespace std;

class UnionSet
{
public:
    UnionSet(size_t n)
    {//构造函数,初始每个元素初始为-1
        v.resize(n+1,-1);
    }

    int Find(size_t x)
    {//查找x的根结点
        size_t tmp = x;
        while (v[tmp] >= 0)
        {
            tmp = v[tmp];
        }

        return tmp;
    }

    void Union(size_t x, size_t y)
    {//合并两个元素
        size_t root1 = Find(x);
        size_t root2 = Find(y);

        if (root1 == root2)
            return;

        v[root1] += v[root2];
        v[root2] = root1;
    }

    size_t IsInSet(size_t x, size_t y)
    {//判断两个元素是否在一棵树下
        return Find(x) == Find(y);
    }

    size_t SetCount()
    {//计算树的数量
        size_t count = 0;
        for (int i = 0; i < v.size(); ++i)
        {
            if (v[i] > 0)
                count++;
        }

        return count-1;
    }

private:
    vector<int> v;
};

int Test()
{
    int arr[3][2] = { {1,2},{2,3},{4,5} };
    UnionSet U(5);
    for (size_t i = 0; i < 3; ++i)
    {
        U.Union(arr[i][0], arr[i][1]);
    }
    cout << U.SetCount() << endl;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值