图也是一种 非线性结构,是由多个顶点组成的关系集合组成的一种数据结构。图可以分为两种,无向图和有向图。


★图的定义:

图—并查集(解决朋友圈问题)_第1张图片


★典型问题:

      利用图能够解决很多问题,这里有一个较为典型的问题,假如已知有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个朋友圈。



★解题思路:

      首先,先介绍一个概念:并查集。并查集就是将N个不同元素分成一组不想交的集合,开始时,每个元素就是一个集合,然后按规律将两个集合进行合并。具体的做法如下:    

     设定一个有N个元素的数组,将每个元素对应的位置都置为-1,即就是先让其没有相交,然后根据题目中给出的朋友关系,将根节点元素对应的位置减1,将与根节点是好友的元素对应的位置更改为根节点元素,按照这样的方式,将所有的关系都进行对应,最后数组中如果是负数,所对应的元素就是根节点。


图—并查集(解决朋友圈问题)_第2张图片


★具体代码:

#pragma once
#include 
//实现图  ——并查集
/*
主要功能:给定一个范围,能够确定朋友圈的个数
*/

class UnionFindSet
{
public:
     UnionFindSet(size_t size)    //构造函数
          :_n(size)
          , _set(new int[size])
     {
          for (int i = 0; i < size; i++)     //将_set中的数据最先初始化为-1
          {
               _set[i] = -1;
          }
     }
     
     void Union(int root1, int root2)    //结合两个根节点
     {
          assert(_set[root1] < 0);
          assert(_set[root2] < 0);
          _set[root1] += _set[root2];
          _set[root2] = root1;
     }
     
     size_t FindRoot(int child)
     {
          if (_set[child] < 0)   //负数证明这个节点就是根节点
          {
               return child;
          }
          int num = _set[child];
          while (num >= 0)
          {
               num = _set[num];
          }
          return num;
     }
     
     void print()
     {
          int root = 0;
          for (int i = 0; i < _n; i++)
          {
               if (_set[i] < 0)
               {
                    root++;
               }
          }
          cout << "输出有几个朋友圈:" << root << endl;
     }
     
protected:
     int* _set;    //数组指针
     size_t _n;     //给定范围数据的个数
};

void Test()
{
 UnionFindSet ht(10);
 ht.Union(0, 6);
 ht.Union(0, 7);
 ht.Union(0, 8);
 ht.Union(1, 4);
 ht.Union(1, 9);
 ht.Union(2, 3);
 ht.Union(2, 5);
 ht.print();
}