【Data structure】Union search

  Let’s look at a question first: if it is known that there are n people and m pairs of friends (stored in the array r), if two people are direct or indirect friends (friends of friends of friends…), then they are considered to belong to the same circle of friends , please write a program to find out how many circles of friends there are in n people.
  For example: n=5, m=3, r={{1,2},{2,3},{4,5}}, indicating that there are 5 people, 1 and 2 are friends, 2 and 3 are friends, 4 and 5 are friends, then 1, 2, and 3 belong to one circle of friends, and 4 and 5 belong to one circle of friends, resulting in 2 circles of friends.
  
  The first method: we can create 3 sets - s1, s2, s3 correspond to 3 pairs of relationships, find 0 and 1 in s1 in s2 and s3 respectively, if found, insert the elements in s2 into s1, And clear s2; count the number of non-empty sets to get the number of circles of friends.

What is a union check?


  In some set application problems with N elements, we usually make each element form a single-element set at the beginning, and then merge the sets of elements belonging to the same group in a certain order. Iteratively finds which collection an element is in. Union search set is a tree data structure, used to deal with the merge and query problems of some sets that do not want to intersect. In use, it is often represented by a forest.
  Each set is represented by a tree, and each node of the tree represents an element of the set. At the beginning, each element is its own set, so it is a forest. Similar to the heap, we use the subscript of an array to represent the element name, and the i-th array element represents the root node of the collection where it is located. The subscript of the root node of the tree represents the name of the set, and the value of the root node represents the number of elements in the set.

The main operations are:

  1. Initialization: This step is usually only performed once
  2. Find: Find the collection where the element is located, that is, the root node
  3. Merge: Merge the sets where the two elements are located​

As an example:
Suppose a set s is: s={0,1,2,3,4,5,6,7,8,9}.
1" Initialization: We all initialize the root node of each tree with -1
write picture description here

0 1 2 3 4 5 6 7 8 9
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1

2" is merged into the following 3 sets:
s1={0,3,4,5,6}; s2={1,7,8}; s3={2,9};
the tree structure is as follows:
write picture description here
according to the above tree The structure of , assuming our array is named a, what we need to do is, a[0]=a[0]-a[3](因为是负数,所以需要减操作); a[3]=0(3的父结点);
and so on, we can get an array like this:

0 1 2 3 4 5 6 7 8 9
-5 -3 -2 0 0 0 0 1 1 2

We can find that the absolute value of the negative number in the array represents the number of elements in the set; the number of negative numbers represents the number of sets; the non-negative number represents the parent node corresponding to its subscript.

3" If you want to merge the set corresponding to 6 with the set corresponding to 9. What should be done?
write picture description here
First, judge whether the parent nodes corresponding to 6 and 9 are the same node. If they are the same, it means that they are in the same set; if they are not the same, first find the parent nodes of 6 and 9— -0 and 2, then execute a[0]=a[0]-a[2]; a[2]=0 (the parent node of 2); the
array becomes:

0 1 2 3 4 5 6 7 8 9
-7 -3 0 0 0 0 0 1 1 2

The code for the collection is as follows :

#include <iostream>
#include <vector>
using namespace std;
class UnionFind
{
public:
     UnionFind(size_t size)
          : _set(size, -1)//构造函数
     {
          // _set.resize(size, -1);//用-1初始化   3中方法都可以实现
          // _set.assign(size, -1);
     }
     void Union(int x1, int x2)
     {
          int root1 = FindRoot(x1);
          int root2 = FindRoot(x2);
          if (root1 != root2)
          {
               _set[root1] += _set[root2];
               _set[root2] = root1;
          }
     }
     size_t Count()// 合并后的个数
     {
          size_t count = 0;
          for (size_t i = 0; i < _set.size(); i++)
          {
               if (_set[i] < 0)
                    count++;
          }
          return count;
     }
private:
     int FindRoot(int x)// 查找父结点
     {
          while (_set[x] >= 0)
               x = _set[x];
          return x;
     }
private:
     vector<int> _set;
};

At this point, looking at the question we asked at the beginning, it will become very simple:

size_t friends(const int n, const int m, int r[][2])
{
     UnionFind u(n + 1);//根据题目给出的编号,为方便解决题目,所以没有使用0号位置
     for (int i = 0; i < m; i++)
          u.Union(r[i][0], r[i][1]);
     return u.Count() - 1;//因为0号位置没有使用,而初始化的-1为负数,所以要减1
}
int main()
{
     const int m = 3;
     const int n = 5;
     int r[3][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 } };
     cout << "朋友圈个数:" << friends(n, m, r) << endl;
     return 0;
}

operation result:
write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325626990&siteId=291194637