そして、グラフの接続問題を解決するためのチェックセット

1. 定義

コンピューター サイエンスでは、ディスジョイント セット(英語: Disjoint-set data structure、文字通りディスジョイント データ構造として翻訳) は、いくつかのディスジョイント セット (繰り返される要素のない一連のセット) のマージと問い合わせの質問に対処するために使用されるデータ構造です。 . ユニオン検索では、次の操作がサポートされています。
クエリ: 要素が属している集合に対するクエリで、通常は集合内の「代表要素」を返します。この操作は、2 つの要素が同じセットにあるかどうかを判断するためのものです。
Merge : 2 つのコレクションを 1 つにマージします。
Add : 新しい要素を持つ新しいコレクションを追加します。追加操作は、クエリおよびマージ操作ほど重要ではなく、見過ごされがちです。
クエリとマージ操作をサポートしているため、ユニオン検索は英語でUnion -find データ構造またはMerge -find setとも​​呼ばれます。

2. 確認と設定

  • 「グラフの動的接続」の問題を解決するために使用できます。
  • 特定の問題を解決する場合、適切な「仮想頂点」を設定することで要素を分類できます。つまり、同じクラスの要素が互いに接続されているため、すべての要素を異なる接続ドメインに分割してから、他の操作を実行できます。 ;
  • コレクションの最下層は、1 次元配列を使用して複数の「フォレスト」を格納しますが、実際には特定の頂点の祖先頂点のみを考慮し、頂点の直接の親ノードとして祖先頂点を取得します。
  • 基になる 1 次元配列の添字は、頂点の祖先頂点が格納されている各頂点の番号に対応します。
  • 最初は、頂点の直接の親は頂点そのものです。
  • マージ検索コードには 3 つの部分が含まれます: 1) 初期化: 親ノードの配列を作成する; 2) クエリ操作: 頂点の親頂点をクエリする; 3) マージ操作: 2 つの異なるサブツリーをルート ノードの 1 つにマージするマージされたツリーのルート ノード。
  • 2 つの頂点が接続されている場合、それらの直接の親ノードは同じです。

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

3. テンプレートコード

package com.northsmile.union;


/**
 * @author NorthSmile
 * @version 1.0
 * @date 2023/4/19&21:46
 * 并查集模板代码
 */
public class Template {
    
    
    // 存储编号为i的顶点的祖先顶点编号
    public int[] parent;
    // 记录连通分量的数量
    public int count;
    /**
     * 初始化
     */
    public Template(int n){
    
    
        // 初始化节点数量为n,节点编号依次为0~n-1
        parent=new int[n+1];
        for (int i=1;i<=n;i++){
    
    
            // 初始状态:自己做自己的父节点
            parent[i]=i;
        }
        count=n;
    }

    /**
     * 查询某节点的祖先节点
     * @param v
     * @return
     */
    public int find(int v){
    
    
        // 说明此时该节点独立
        if (parent[v]==v){
    
    
            return v;
        } else {
    
    
            // 无路径压缩
//            return find(parent[v]);
            // 路径压缩
            parent[v]=find(parent[v]);
            return parent[v];
        }
    }

    /**
     * 合并顶点v、w
     * @param v
     * @param w
     */
    public void union(int v,int w){
    
    
        // 分别确定两顶点的祖先顶点
        int vP=find(v);
        int wP=find(w);
        if (vP==wP){
    
    
            return;
        }
        count--;
        // 以wP作为vp的父顶点,此时v的祖先顶点更新为wP,也就是将v、w为根节点的树合并为一棵树
        parent[vP]=wP;
    }

    /**
     * 判断v、w之间是否连通
     * 如果二者连通,他们的祖先顶点一定相同
     * @param v
     * @param w
     * @return
     */
    public boolean connected(int v,int w){
    
    
        // 分别确定两顶点的祖先顶点
        int vP=find(v);
        int wP=find(w);
        return vP==wP;
    }

    /**
     * 返回连通域的数量
     * @return
     */
    public int count(){
    
    
        return count;
    }
}

4. レッコのサンプル問題

4.1 オファー II を指す 118. 余分なエッジ

ソード ポインター オファー II 118. エクストラ エッジ

ツリーは、接続された非巡回無向グラフと見なすことができます。n 個のノード (ノード値 1 ~ n) のツリーにエッジを追加した後のグラフを指定します。追加されたエッジの 2 つの頂点は 1 と n の間に含まれており、この追加のエッジはツリー内の既存のエッジに属していません。グラフの情報は長さnの二次元配列edgesに記録されており、edge[i] = [ai,bi]はグラフのaiとbiの間に辺があることを意味します。削除できるエッジを見つけてください. 削除後、残りの部分は n ノードの木になる可能性があります. 複数の回答がある場合は、配列のエッジで最後に発生するエッジを返します。

問題解決のアイデア:

  • ツリー内の任意の 2 つのノードが接続されています。
  • n ノードのツリーには n-1 個のエッジがあります。
  • エッジ配列の長さは n で、多くても 1 つのエッジが冗長であることを示します。
  • エッジを形成する 2 つのノードが既に接続されている場合は、2 つの直接の親ノードが同じであることを意味し、この時点で 2 つのノードがマージされている場合は、このエッジが冗長であることを意味します。
class Solution {
    
    
    /**
     *
     * @param edges
     * @return
     */
    public int[] findRedundantConnection(int[][] edges) {
    
    
        int n=edges.length;
        UFData uf = new UFData(n);
        for (int i=0; i<n;i++){
    
    
            if (uf.find(edges[i][0])!=uf.find(edges[i][1])) {
    
    
                uf.union(edges[i][0], edges[i][1]);
            }else {
    
    
                return edges[i];
            }
        }
        return new int[0];
    }
}

class UFData{
    
    
    public int[] parent;
    public UFData(int n){
    
    
        parent=new int[n+1];
        for (int i=1;i<=n;i++){
    
    
            parent[i]=i;
        }
    }

    public int find(int v){
    
    
        if (v==parent[v]){
    
    
            return v;
        }else{
    
    
            parent[v]=find(parent[v]);
            return parent[v];
        }
    }

    public void union(int v,int w){
    
    
        int vP=find(v);
        int wP=find(w);
        if (vP==wP){
    
    
            return;
        }
        parent[vP]=wP;
    }

    public boolean connected(int v,int w){
    
    
        return find(v)==find(w);
    }
}

4.2 リトゥオ 695. 島の最大面積

695.島の最大面積

サイズ mxn のバイナリ マトリックス グリッドが与えられます。島は隣接する 1 の組み合わせ (土地を表す) であり、ここでの「隣接」とは、2 つの 1 が
水平方向または垂直方向の 4 方向に隣接している必要があります。グリッドの 4 つのエッジはすべてゼロ (水を表す) で囲まれていると想定できます。島の面積は、値が 1 の島の
セルの数です。グリッド内の最大の島の面積を計算して返します。島がない場合、返される面積は 0 です。

class Solution {
    
    
    public int maxAreaOfIsland(int[][] grid) {
    
    
        int m=grid.length;
        int n=grid[0].length;
        UFData uf = new UFData(m * n);
        int[][] directions=new int[][]{
    
    {
    
    -1,0},{
    
    1,0},{
    
    0,-1},{
    
    0,1}};  // 上下左右
        boolean haveUnion=false;
        boolean haveOne=false;
        for (int i=0;i<m;i++){
    
    
            for (int j=0;j<n;j++){
    
    
                if (grid[i][j]==1){
    
    
                    haveOne=true;
                    // 连接邻域
                    for (int k=0;k<4;k++){
    
    
                        int cR=i+directions[k][0];
                        int cC=j+directions[k][1];
                        if (cR<0||cR>=m||cC<0||cC>=n){
    
    
                            continue;
                        }
                        if(grid[cR][cC]==1){
    
    
                            haveUnion=true;
                            uf.union(i*n+j,cR*n+cC);
                        }
                    }
                }
            }
        }
//        System.out.println(Arrays.toString(uf.size));
        return haveOne?haveUnion?uf.maxArea:1:0;
    }

    private static class UFData{
    
    
        public int[] parent;
        public int[] size;
        public int maxArea;
        public UFData(int n){
    
    
            parent=new int[n];
            size=new int[n];
            Arrays.fill(size,1);
            for (int i=0;i<n;i++){
    
    
                parent[i]=i;
            }
        }

        public int find(int v){
    
    
            if (v==parent[v]){
    
    
                return v;
            }else{
    
    
                parent[v]=find(parent[v]);
                return parent[v];
            }
        }

        public void union(int v,int w){
    
    
            int vP=find(v);
            int wP=find(w);
            if (vP==wP){
    
    
                return;
            }
            parent[vP]=wP;
            size[wP]+=size[vP];
            maxArea=Math.max(maxArea,size[wP]);
        }

        public boolean connected(int v,int w){
    
    
            return find(v)==find(w);
        }
    }
}

参考文献:

  • マイク氏がアルゴリズムについて語る
  • labuladong アルゴリズムチートシート
  • ウィキペディア

おすすめ

転載: blog.csdn.net/qq_43665602/article/details/130230149