[Data Structure and Algorithm] Union Search

1. The concept of union search

The union search set is a tree structure. The so-called union search means that when we have a node, we can know which set the node belongs to .

Take an example to understand the following: During the Warring States period, there were many countries, and they would fight each other, so now there are two people who don't know each other, how do they know whether the other party is an enemy or a friend?

Now there is a method: every country has a king, we only need to know our superiors, and the superiors know his superiors, and we can find out who his king is, so that we can know whether two people are the same of a country.

2. Realization of union check

First of all, we need an integer array p[]which records the predecessor nodes of each node.
Then find(x)a function is needed, which is used to find which set the specified node x belongs to.
The function join(x1,x2)is used to merge two nodes x1 and x2

2.1 Implementation of find()

It said above that if we want to determine who our king is, we can ask upwards layer by layer.
So we can use find to find the root of a node.

int find(int x)					
{
    
    
	while(p[x] != x)// 一层层的向上找
		x = p[x];				
	return x;					
}

2.2 Path Compression Algorithm

We found that it would be too time-consuming to look up layer by layer every time, so we can compress the path during the search process.
insert image description here

int find(int x)
{
    
    
    // 路径压缩
    if(p[x] != x) p[x] = find(p[x]);//递归出口:x的上级为 x本身,即 x为根结点  
    return p[x];
}

Note that there is no compression effect when searching for the first time here, and the effect will only be effective the second time and later.

2.3 Implementation of join()

Join is to put two originally unrelated nodes into a set. We can borrow the find function for specific implementation. For this, join(x1,x2)we only need to make the superior of x1 become the superior of x2.
insert image description here

void join(int x1, int x2)
{
    
    
    p[find(x1)] = find(x2);
}

3. Application of union search

3.1 Example: Merging Sets

topic link

Title description:

There are a total of n numbers, numbered from 1 to n, and each number is in a set at the beginning.
Now there are m operations to be performed, and there are two types of operations:
M a b, merge the sets where the two numbers numbered a and b
are located, and ignore this operation if the two numbers are already in the same set; ,
Q a bquery the numbers a and b Whether the two numbers of b are in the same set;

input format

The first line enters the integers n and m.
Next m lines, each line contains an operation instruction, the instruction is one of Ma ab or Q ab.

output format

For each query command Q ab, a result must be output, if a and b are in the same set, then output Yes, otherwise output No.
Each result occupies one line.

data range

1≤n,m≤1e5

Input sample:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

Sample output:

Yes
No
Yes

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int p[N];

int find(int x)
{
    
    
    // 路径压缩
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void join(int x1, int x2)
{
    
    
    p[find(x1)] = find(x2);
}

int main()
{
    
    
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        p[i] = i;
    }
    while(m--)
    {
    
    
        char op;
        int x1, x2;
        cin >> op >> x1 >> x2;
        if(op == 'M')
        {
    
    
            join(x1, x2);
        }
        else
        {
    
    
            if(find(x1) == find(x2))
            {
    
    
                cout << "Yes" << endl;
            }
            else
            {
    
    
                cout << "No" << endl;
            }
        }
    }
    return 0;
}

3.2 Example: the number of midpoints in connected blocks

Title description:

Given an undirected graph containing n vertices (numbered 1∼n), initially there are no edges in the graph.
Now there are m operations, there are three types of operations:
C a b, connect a side between point a and point b, a and b may be equal;
Q1 a b, ask whether point a and point b are in the same connected block, a and b may be Equal;
Q2 a, ask the number of points in the connected block where point a is located;

input format

The first line enters the integers n and m.
Next m lines, each line contains an operation instruction, the instruction is one of C ab, Q1 ab or Q2 a.

output format

For each query instruction Q1 ab, if a and b are in the same connected block, output Yes, otherwise output No.
For each query command Q2 a, output an integer indicating the number of points in the connected block where point a is located, and each result occupies one line.

data range

1≤n,m≤1e5

Input sample:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

Sample output:

Yes
2
3

Analysis of ideas:
The difference between this question and the previous one is that here we need to ask how many elements a collection has. Our method is to add a cnt array, and let the root node of each collection record how many elements there are in this collection .
The specific approach is jointo process the cnt array in , because the search finddoes not change the number of elements in the collection.

void join(int x1, int x2)
{
    
    
    cnt[find(x2)] += cnt[find(x1)];
    p[find(x1)] = find(x2);
}

It should be noted here that the order cannot be changed. If put p[find(x1)] = find(x2);in the front, the root of find(x1) will change . So the cnt array must be processed first.

#include <iostream>
#include <string>

using namespace std;

const int N = 1e5 + 10;
int p[N], cnt[N];

int find(int x)
{
    
    
    if(p[x] != x)
    {
    
    
        p[x] = find(p[x]);
    }
    return p[x];
}

void join(int x1, int x2)
{
    
    
    cnt[find(x2)] += cnt[find(x1)];
    p[find(x1)] = find(x2);
}

int main()
{
    
    
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
    
    
        p[i] = i;
        cnt[i] = 1;
    }
    while(m--)
    {
    
    
        string op;
        int a, b;
        cin >> op;
        if(op == "C")
        {
    
    
            cin >> a >> b;
            if(find(a) != find(b))
            {
    
    
                join(a, b);
            }
        }
        else if(op == "Q1")
        {
    
    
            cin >> a >> b;
            if(find(a) == find(b))
            {
    
    
                cout << "Yes" << endl;
            }
            else
            {
    
    
                cout << "No" << endl;
            }
        }
        else
        {
    
    
            cin >> a;
            cout << cnt[find(a)] << endl;
        }
    }
    return 0;
}

Four. Summary

1️⃣ We generally use an element in the set to represent the set , and this element is called the representative element of the set.
2️⃣ For each element x, pre[x]store the parent node of x in the tree structure (if x is the root node, then let pre[x] = x)

In general, a union search corresponds to two operations:

  1. Find function (Find() function)
  2. Merge aggregate function (Join() function)


Guess you like

Origin blog.csdn.net/qq_66314292/article/details/130090824