c++ and check set (detailed summary)

To re-talk the old saying, look at the definition first

Union query set is a tree-type data structure, used to deal with some disjoint sets (disjoint sets) merge and query problems. It is often represented by forest in use.

First of all, we must understand some concepts:
what is a tree, what is a forest (the tree is called a forest hh), and what is a collection.
These questions are knowledge in other areas, so they won’t be too burdensome. Those who don’t understand it are recommended to understand first.

Let’s get to the point

1. And collect

First of all, I personally think that the union search set is logically a forest. The forest is composed of one or more trees. For example,
Insert picture description here
the three trees can form a forest, and this forest can be called the union search set. A tree can be called the union search component, which is a logical understanding

Explicitly understand that in most cases, the combined query set is stored in the form of an array

Some of the following properties

  • Each union search component (that is, each tree) has a root node. For example, the root nodes of the three trees above are 1, 2, and 10 respectively.
  • The root nodes of the nodes that belong to the same union check component are the same. For example, the root nodes of 6, 7, and 8 are all 10, so these three nodes are in the same union check component, that is, the same On the tree
  • And each component is independent of each other and does not affect each other!
  • And the value of all nodes in the set must be different from each other

2. Commonly used methods of combined search

Before talking about the method, we need to define some and collect the data that needs to be used

data storage Functions and examples
parent[] parent[ i] = j means that the parent node of node i is j (for example, the tree whose root node is 10 above has parent[ 7] = 6, parent[ 6] = 10… )
count It is a variable of type int, which indicates how many union check components are in the union check set, that is to say, how many class trees are in the union check set ( for example, there are three trees in the above example, so count=3 )

We first define and check the data structure code (here we use c++)

class DisjointSets{
    
    
    public:
         // 给个默认值,默认是10
        int count = 10;
        // vector的好处是可以动态修复数组大小
        vector<int> parent;
        // 类的有参构造方法
        DisjointSets(int count){
    
    
            this->count = count;
            // 对parent数组进行初始化
            for(int i=0;i<=count;++i)
                // 默认这个森林有count棵树,
                //而且每棵树只有一个节点,也就是
                // 根结点,默认根结点的父节点是根结点本身
                parent[i] = i;
        }
        // 类的析构函数,销毁类实例前调用
        ~DisjointSets(){
    
    }
        
        // 并查集的方法定义-----------------
        // 查找一个节点所属树的根节点
        int findParentNode(int x);
        // 合并两棵树
        void unionSetNode(int x,int y);        
};

2.1. Union search set-find the root node of a node

int DisjointSets::findParentNode(int x){
    
    
    // 如果x的父节点还是x,说明x就是根节点
    if(x == this->parent[x]) return x;
    // 否则继续找
    return findParentNode(this->parent[x]);
}

2.2. Union check set-merge two union check set components (merge two trees)

Sometimes we need to merge some combined search components to meet the demand
Insert picture description here
. Merge the tree with root node 2 to the tree with root node 1.
After the merging is completed, the parent node of root node 2 is no longer 2, but 1, as follows
Insert picture description here
. Before merging, we also need to determine whether the two nodes that need to be merged are the same and check the set component (on the same tree)
code as follows:

void DisjointSets::unionSetNode(int x,int y){
    
    
    // 先分别获取到 x 节点和 y节点 所属树的根节点
    int root_x = this->findParentNode(x);
    int root_y = this->findParentNode(y);
    // 如果两个节点的根节点相等,就不需要合并,是同一颗树的节点
    if(root_x == root_y) return;
    // 如果不相等,由于是y所属树合并到 x所属树上
    // 所以让 y所属树的根节点的父节点赋值为x所属树的根节点
    this->parent[root_y] = root_x;
    // 同时, 此时森林少了一颗树
    --this->count;
}

2.3, and search set to find the root node optimization-path compression algorithm

So what is path compression? First, let’s look at the traditional method of finding the root node of the tree to which a node belongs. This is
Insert picture description here
equivalent to the root node of the tree to which we 4 belong, and we must traverse all the paths that can be taken.
It’s okay if we only do it once, but if we want to find the root node of 4 repeatedly, is it a waste of time that we have to repeat it every time, so we found the root node information of 4 once, and use something like Based on the idea of ​​the memo, the parent node of 4 is directly promoted from 3 to 1, so that you don't have to repeat it again next time you find the
Insert picture description here
code as follows:

int DisjointSets::findParentNode(int x){
    
    
    // 路径压缩改良版的查找
    // 如果 x的父节点是本身,说明x是根节点,退出循环,返回x
    while( x != this->parent[x] ){
    
    
        // 将 x 的父节点赋值为 x的父节点的父节点
        this->parent[x] = this->parent[this->parent[x]];
        // 改变此时x的值为 x的父节点
        x = this->parent[x];
    }
    return x;
}

Here are two leetcode topics to practice hand
547. The number of provinces is
839. Similar string group

The code answers to the two questions are:

class Solution {
    
    
public:
    const static int N = 205;
    int parent[N];
    int count = 0;

    // 查找 x 的根结点
    int find(int x){
    
    
        return x==parent[x]?x:find(parent[x]);
    }
    // 合并,把 y 合并到 x内
    void megre(int x,int y){
    
    
        int root1 = find(x);
        int root2 = find(y);
        // 判断 x与y 是否位于同一个并查集分量内,是就返回,不需要合并
        if(root1 == root2) return;
        // y的根结点的父亲更新为x的根结点
        parent[root2] = root1;
        // 合并成功说明少了一个点
        --count;
    }
    // 初始化并查集数据
    void init(int n){
    
    
        // 所有点的根结点初始默认是本身
        for(int i=0;i<n;++i)
            parent[i]=i;
        // 初始count大小就是n
        count = n;
    }

    // 并查集的 第一题 实战
    int findCircleNum(vector<vector<int>>& isConnected) {
    
    
        int n = isConnected.size();
        // 初始化并查集
        init(n);
        for(int i = 0;i<n;++i){
    
    
            for(int j=0;j<n;++j){
    
    
                if(isConnected[i][j]==1){
    
    
                    megre(i,j);
                }
            }
        }
        return count;
    }
};
class Solution {
    
    
public:
    const static int N = 301;
    int parent[N];
    int count=0;
    /// 路径压缩
    int find(int x){
    
    
        while(x != parent[x]){
    
    
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    }
    void union_set(int x,int y){
    
    
        int r1 = find(x);
        int r2 = find(y);
        if(r1==r2) return;
        parent[r2]=r1;
        --count;
    }
    void init(int n){
    
    
        count = n;
        for(int i=0;i<n;++i)
            parent[i]=i;
    }
    bool judge(string &s1,string &s2){
    
    
        int k = 0;
        for(int i=0;i<s1.size();++i)
            if(s1[i]!=s2[i])
                ++k;
        if(k<=2) return true;
        else return false;
    }
    // 考查并查集
    int numSimilarGroups(vector<string>& strs) {
    
    
        int n = strs.size();
        init(n);
        for(int i=0;i<n;++i)
            for(int j=i+1;j<n;++j)
                if(judge(strs[i],strs[j]))
                    union_set(i,j);
        return count;
    }
};

Guess you like

Origin blog.csdn.net/YSJ367635984/article/details/113504723
Recommended