c ++とチェックセット(詳細な要約)

古いことわざをもう一度話すには、最初に定義を見てください

ユニオンクエリセットはツリータイプのデータ構造であり、いくつかの互いに素なセット(互いに素なセット)のマージとクエリの問題を処理するために使用されます。多くの場合、使用中の森林によって表されます。

まず
、木とは何か、森とは何か(木は森と呼ばれます)、コレクションとは何かという概念を理解する必要があります。
これらの質問は他の分野の知識であるため、そうではありません。わからない人はまず理解することをお勧めします。

要点をつかみましょう

1.そして収集する

まず、個人的にはユニオンサーチセットは論理的には森だと思います。森は1本以上の木で構成されています。たとえば
ここに画像の説明を挿入
、3本の木が森を形成し、この森をユニオンサーチセットと呼びます。Aツリーは、論理的な理解であるユニオン検索コンポーネントと呼ぶことができます

ほとんどの場合、結合されたクエリセットは配列の形式で保存されることを明確に理解してください

次のプロパティの一部

  • 各ユニオン検索コンポーネント(つまり、各ツリー)にはルートノードがあります。たとえば、上記の3つのツリーのルートノードは、それぞれ1、2、および10です。
  • 同じユニオンチェックコンポーネントに属するノードのルートノードは同じです。たとえば、6、7、および8のルートノードはすべて10であるため、これら3つのノードは同じユニオンチェックコンポーネントにあります。同じ木に
  • そして、各コンポーネントは互いに独立しており、互いに影響を与えません!
  • また、セット内のすべてのノードの値は互いに異なっている必要があります

2.一般的に使用される複合検索の方法

メソッドについて説明する前に、いくつかを定義し、使用する必要のあるデータを収集する必要があります

データストレージ 関数と例
親[] parent [i] = jは、ノードiの親ノードがjであることを意味します(たとえば、ルートノードが上記の10であるツリーには、parent [7] = 6、parent [6] = 10…)があります。
カウント これはint型の変数であり、ユニオンチェックセットに含まれるユニオンチェックコンポーネントの数、つまり、ユニオンチェックセットに含まれるクラスツリーの数を示します(たとえば、上記の例では3つのツリーがあります。したがって、count = 3

まず、データ構造コードを定義して確認します(ここでは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。ユニオン検索セット-ノードのルートノードを検索します

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

2.2。ユニオンチェックセット-2つのユニオンチェックセットコンポーネントをマージします(2つのツリーをマージします)

需要を満たすために、いくつかの結合された検索コンポーネントをマージする必要がある場合があり
ここに画像の説明を挿入
ます。ルートノード2のツリーをルートノード1のツリーにマージします。
マージが完了すると、ルートノード2の親ノードは2ではなく1になります
ここに画像の説明を挿入
。マージする前に、マージする必要のある2つのノードが同じかどうかを判断し、設定されたコンポーネントを確認する必要があります(同じツリー上)
次のようなコード:

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、およびルートノード最適化パス圧縮アルゴリズムを見つけるための検索セット

では、パス圧縮とは何ですか?最初に、ノードが属するツリーのルートノードを見つける従来の方法を見てみましょう。これは
ここに画像の説明を挿入
、私たち4が属するツリーのルートノードに相当しすべてをトラバースする必要があります。とることができるパス。
一度だけでも大丈夫ですが、4のルートノードを繰り返し見つけたいのなら、毎回繰り返すのは時間の無駄なので、4のルートノード情報を一度見つけて、メモのアイデアに基づいて、4の親ノードは3から1に直接昇格されるため、次に次の
ここに画像の説明を挿入
コードを見つけたときにもう一度繰り返す必要はありません

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;
}

ハンド
547を練習するための2つのリートコードトピックがあります。州の数は
839です。同様の文字列グループ

2つの質問に対するコードの回答は次のとおりです。

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;
    }
};

おすすめ

転載: blog.csdn.net/YSJ367635984/article/details/113504723