数据结构--并查集

引:例题

小米公司一道面试题:
假如已知有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属于另一个朋友圈,共有两个朋友圈。请写出相应程序。

并查集

定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

实现:
用小米面试题为例,我们可以用树形结构表示不同的朋友圈。
1,2,3属于一个朋友圈。
4,5属于一个朋友圈。
这里写图片描述
我们可以用一个大小的6的数组表示。

(这里如果用大小为6的数组,则最后朋友圈个数结果应减1,因为数组下标为0的多加了一次。也可以用大小为5的数组)

数组的初始值全部为-1。

(这里-1,1为此数的结点个数,负号起到一个标识区分的作用。)

当两个人为一个朋友圈时,第一个人下标所在的值为两个下标所在值的和,第二个人的值为第一个人的下标。

当1,2为一个朋友圈时:
这里写图片描述
当2,3为朋友圈时:

这时查到2下标处的值不为负数,则应该继续查找2的值所对应的下标处,直到找到跟为止。
找到此数字,再将两者相加

这里写图片描述

其余好友关系同上
最后,统计朋友圈的个数,只需要统计数组中有几个负数即可。

代码实现:

#pragma once

#include <stdio.h>
#include <assert.h>
#include <vector>
using namespace std;

class Union_Find_Sets
{
public:
    Union_Find_Sets(size_t size)
    {
        s.resize(size, -1);
    }

    int FindRoot(int x)
    {
        int root = x;
        while (s[root] >= 0)
        {
            root = s[root];
        }
        return root;
    }

    void Union(int x1,int x2)
    {
        //先找X1与X2的根
        int root1 = FindRoot(x1);
        int root2 = FindRoot(x2);

        //如果两个元素在同一集合中 则相加会出错
        if (root1 != root2)
        {
            s[root1] += s[root2];
            s[root2] = root1;
        }
    }

    //看两个结点是否属于同一集合 只需要看两个跟是否相同
    bool IsSameSet(int x1,int x2)
    {
        return FindRoot(x1) == FindRoot(x2);
    }

    //看有多少集合数目 只需要看非负数的个数
    size_t SetSize()
    {
        size_t count = 0;
        for (size_t idx = 0; idx < s.size(); idx++)
        {
            if (s[idx] < 0)
                count++;
        }
        return count;
    }

private:
    vector<int> s;
};

void Test()
{
    Union_Find_Sets st(10);
    st.Union(0, 3);
    st.Union(6, 8);
    st.Union(3, 6);
    st.Union(4, 1);
    st.Union(1, 5);
    st.Union(2, 7);
    st.Union(7, 9);

    cout<<st.IsSameSet(0, 9)<<endl;
    cout<<st.SetSize()<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值