Union of data structures (including code implementation)

content

1. Related concepts of union search

2. Relevant operations of union search and its realization


1. Related concepts of union search

Union search set is a tree data structure used to deal with the merge and query problems of some disjoint sets.

The idea of ​​union search is to use an array to represent the entire forest (parent), and the root node of the tree uniquely identifies a set. As long as we find the root of an element, we can determine which set it is in.

2. Relevant operations of union search and its realization

The idea of ​​set lookup is to determine the group in which the vertex is located by marking.

So for a graph with n points and m edges, we need to create a new array f (which can be understood as father) of length n, where f[n] represents the "representative person" of the gang at point n. If the "representatives" are the same, the two points belong to the same gang.

In the beginning, each vertex is disconnected from each other, so each vertex belongs to a gang alone, and each vertex should be the "representative" of its own gang, so we set the initial value of f[n] Assign it to n.

 And and gang:

For example, we want to merge 1 and 3 to make them a gang. Since it has become a gang, there must be a spokesperson, then the stronger party must be the spokesperson. Now the strength of 1 and 3 is the same. Suppose we let 3 be the spokesperson:

 Just now we merged 3 and 1, now we need to merge 3 and 2. If combined according to f[a] = b, then f[3] is assigned the value 2. In this way, the original value of f[3] 1 is overwritten, that is to say, the gangs of 1 and 3 are forcibly "disassembled". So we need to define f[a's gang representative] = (b's gang representative).

Determine if two teams are represented by the same:

We can do the same by looking up whether their representatives are equal. See the generic code below:

#include<unordered_map>
#include<iostream>
#include<stack>
#include<list>
#include<vector>
using namespace std;

template<class V>
struct Node {
	Node(V v)
		:val(v)
	{}
	V val;
};

template<class V>
class UnionFind {
public:
	
	UnionFind(int N) {
	        for(int value=1;value<=N;value++){
			Node<V>* node = new Node<V>(value);//创建一个节点
			nodes.insert(make_pair(value, node));//插入
			parent.insert(make_pair(node, node));//一开始自己指向自己
			sizeMap.insert(make_pair(node, 1));//一开始集合的个数为1
            }
		}
    
	Node<V>* findFather(Node<V>* cur) {//寻找其父亲节点
		stack<Node<V>*>path;
		while (cur != parent[cur]) {//知道不能够再往上了
			path.push(cur);//记录这条路径上的节点
			cur = parent[cur];//一直往上走
		}
		while (!path.empty()) {//路径压缩做一个优化我们每次都要找代表人都要一直往上找不如找一次找到过程我将其全部设置好
			parent.insert(make_pair(path.top(), cur));//将其父亲节点都设置为cur
			path.pop();
		}
		return cur;//
  }
	bool isSameSet(V a, V b) {
		if (!nodes[a] || !nodes[b]) {//看这两个集合是否都存在
			return false;
		}
		return findFather(nodes[a]) == findFather(nodes[b]);//查看他们的父亲节点是否相同
     }
	bool Union(V a, V b) {
		if (!nodes[a] || !nodes[b]) {//首先看其是否都存在
			return false;
		}
		Node<V>* aHead = findFather(nodes[a]);//找到各自对应的父亲节点
		Node<V>* bHead = findFather(nodes[b]);
		if (aHead != bHead) {
			int aSetSize = sizeMap[aHead];
			int bSetSize = sizeMap[bHead];
			if (aSetSize >= bSetSize){//a集合的大小大于b集合的大小
				parent[bHead] = aHead;
				sizeMap[aHead] = aSetSize + bSetSize;
				sizeMap.erase(bHead);
				}

			else {
				parent[aHead] = bHead;
					sizeMap[bHead] = aSetSize + bSetSize;//合并
					sizeMap.erase(aHead);//删除
			}

		}
         return true;
	}

private:
	unordered_map<V, Node<V>*>nodes;//存储节点
	unordered_map<Node<V>*, Node<V>*>parent;//节点的父亲
	unordered_map<Node<V>*, int>sizeMap;//每个一集合的个数
};

Let's look at two OJ questions:

Implementation of Union Search

Topic description:

 Since it has been said above, only the code is given here:

#include<unordered_map>
#include<iostream>
#include<stack>
#include<list>
#include<vector>
using namespace std;

class  UnionFind{
  public:
    UnionFind(int n){
        parent.resize(n+1);//存储节点对应的父亲节点
        size.resize(n+1);//存储集合对应的大小
        help.resize(n);//做路径压缩的那个数组
        sets=n;
        for(int i=1;i<=n;i++){
            parent[i]=i;//一开始父亲节点
            size[i]=1;
        }
    }
    int find(int i){
        int hi=0;
        while(i!=parent[i]){
            i=parent[i];
            help[hi++]=i;
        }
        for(hi--;hi>=0;hi--){
            parent[help[hi]]=i;//路径压缩
        }
        return i;
    }
    bool isSameSet(int x,int y){
        return find(x)==find(y);
    }
    void Union(int i,int j){
        int aHead=find(i);//查找他的父亲
        int bHead=find(j);//查找父亲
        if(aHead!=bHead){//如果两个的父亲不相等
            if(size[aHead]>=size[bHead]){//a集合元素大一点也就是力量大一点
                size[aHead]+=size[bHead];
                parent[bHead]=aHead;
                
            }
            else{
                size[bHead]+=size[aHead];
                parent[aHead]=bHead;
            }
            sets--;
        }
    }
    
private:
  vector<int>parent;//存储父亲节点
  vector<int>size;//集合的大小个数
  vector<int>help;//
   int sets;//集合的个数
};

int main(){
    int N,M;
    cin>>N>>M;
 
    UnionFind t(N);
    while(M--){
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==1){
           if(t.isSameSet(x, y)){
             printf("Yes\n");
           }
            else{
             printf("No\n");
            }
        }
        else{
            t.Union(x,y);
        }
         
    }
    return 0;
}

Sword Points Offer II 116. Number of Provinces - LeetCode (leetcode-cn.com)

 Topic description:

Problem solving ideas:

According to the meaning of the title, if isconectin[i][j] are connected, it means that they know each other. At this time, we only need to merge them and finally return the remaining number of sets.

Corresponding code:

class Union
{
public:
    Union(int n)
    {
        size.resize(n);
        parent.resize(n);
        help.resize(n);
        sets=n;
        for(int i=0;i<n;i++)
        {
              parent[i]=i;
              size[i]=1;
        }
    }
     int findfather(int i)
     {
          int hi=0;
          while(parent[i]!=i)
          {
           help[hi++]=i;//将沿途路径所有的节点全部记录下来
            i=parent[i];
          }
        
        for(hi--;hi>=0;hi--)//路径压缩
        {
            parent[help[hi]]=i;
        }
        return i;//返回父亲

     }
      int getSets()//获取集合的数量
      {
          return sets;
      }
      void unoin(int i,int j)//合并两个集合
      {
        int aHead=findfather(i);
        int bHead=findfather(j);
        if(aHead!=bHead)
        {
            if(size[aHead]>=size[bHead])
            {
                size[aHead]+=size[bHead];
                parent[bHead]=aHead;
            }
            else
            {
             size[bHead]+=size[aHead];
             parent[aHead]=bHead;
            }
            --sets;
        }
          
      }
private:
  vector<int>size;
  vector<int>parent;//存储对应的父亲
  vector<int>help;//路径压缩
  int sets;//记录集合的数量
};
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n=isConnected.size();
        Union t(n);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(isConnected[i][j]==1)//说明两个认识
                {
                    t.unoin(i,j);//合并他们两个
                }
            }
        }
        return t.getSets();//返回剩余的集合数
    }
};

Guess you like

Origin blog.csdn.net/qq_56999918/article/details/123761805