互いに素な集合

素集合研究ノート(Python、C言語)

今月は、リートコードをブラッシングするときに問題に3回遭遇し、チェックしました。最初の2回は手放すのが難しすぎました。今日問題が発生した場合は、最初に解決してみてください。この知識は、「チャレンジプログラミングコンペティション」という本で見ました。 "ジュニアのテーブルにメモを書き留めるにはここをクリックしてください。



序文

ユニオン検索は、树型いくつかの不相交集合(disjoint sets)的合并及查询問題に対処するために使用されるデータ構造です。多くの場合、使用中の森林によって表されます。


1.複合検索とは何ですか?

ユニオンチェックセットは、要素のグループ化を管理するために使用されるデータ構造です。
以下の操作を効率的に行うことができます

  • 要素aと要素bが同じグループに属しているかどうかを照会します。
  • 要素aと要素bのグループを組み合わせる

グループ化と対応する例
グループ化と対応する例

第二に、結合された検索セットの構造

そして、セットがであることを確認してください树形结构ただし、二分木ではありません。

各要素はノードに対応し、各グループはツリーに対応します。

複合チェックでは、どのノードがどのノードの親であるか、ツリーの形状などの情報に注意を払う必要はありません。全体的なツリー構造が最も重要です。

1.初期化

初期化

2.合併

下の図のように、あるグループのルートから別のグループのエッジまで、2つのツリーが1つのツリーになります。つまり、2つのグループが1つのグループになります。
ここに画像の説明を挿入

3.お問い合わせ

2つのノードが同じグループにあるかどうかを照会するには、ツリーを上って根节点是否相同、ルートノードが同じである場合は同じグループを照会し、そうでない場合は異なるグループを照会します上図に示すように、(2)と(5)のルートは(1)であり、(7)のルートは(6)であるため、(2)と(5)は同じグループですが、(2 )と(7)は同じグループではありません。

実現すべき注意点

ツリーデータ構造では、劣化が発生すると(バイナリツリーが1次元のリンクリストに縮退する)、時間計算量が非常に高くなります。複合検索では、この方法に従うだけで劣化を回避できます。

  • 樹木ごとに、樹木の高さ(ランク)を記録します。
  • マージするときに、2つのツリーのランクが異なる場合、小さいランクが大きいランクに接続されます。

ここに画像の説明を挿入
さらに、パス圧縮により、連結をより効率的にすることができます。各ノードについて、ルートノードに一度アクセスしたら、このポイントから親にエッジを変更して、ルートに直接接続します。
(7)を照会する必要がある場合は、(7)をルートに直接接続できます。
ここに画像の説明を挿入
2番目より上では、クエリされたノードだけでなく、クエリプロセスで渡されたすべてのノードをルートに直接接続できます。もう一度クエリを実行すると、ルートが誰であるかをすばやく見つけることができます。
以下のように、(2)(3)(4)(5)を(1)に接続します。
ここに画像の説明を挿入
この簡略化方法を使用すると、簡単にするために、ツリーの高さが変更されても、ランクは変更されなくなります。

ユニオンの複雑さを確認してください

2つの最適化を追加した後、結合のチェックの効率は非常に高くなります。n個の要素の和集合に対して演算を実行する複雑さはO(a(n))O(a(n))です。O a n ここでa(n)a(n)a n はアッカーマン関数の逆関数です。これはO(log(n))O(log(n))よりも優れていますO l o g n )はさらに高速です。

ただし、これは「償却された複雑さ」です。毎回満足するわけではなく、何度も繰り返した後、平均的な複雑さは毎回です。

並行チェックセットの実現

配列parは親番号を表します、par [x] = x par [x] = xp a r [ x ]=xxxの場合xは木の根です

int par[MAX_N];    // 父亲
int rank[MAX_N];    // 树的高度

// 初始化n个元素
void init(int n) {
    
    
    for (int i = 0); i < n; i++){
    
    
        par[i] = i;
        rank[i] = 0;
        }
    }

// 查询树的根
int find(int x){
    
    
    if (par[x] == x):
        return x;
    }
    else{
    
    
    return par[x] = find(par[x]);
    }
}

// 查询x和y所属集合
void unite(int x, int y){
    
    
    x = find(x);
    y = find(y);
    if (x == y) return;
    
    if (rank[x] < rank[y]){
    
    
        par[x] = y;
        } else {
    
    
        par[y] = x;
        if (rank[x] == rank[y]) rank[x]++;
    }
}

// 判断x和y是否属于同一集合
bool same(int x, int y){
    
    
    return find(x) == find(y);
}

Pythonの実装

class UnionFind:
    def __init__(self, nums):
        self.par = list(range(len(nums)))
        self.rank = [0] * len(nums)
    
    # 查询树的根   
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            return self.find(self.par[x])
        
    # 查询x个y所属集合   
    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
    
    # 判断x和y是否为同一集合
    def same(self, x, y):
        return self.find(x) == self.find(y)

LeetCode1202の例。文字列内の要素を交換します

トピックリンク::文字列要素を交換します
トピック:
文字列sと文字列内のいくつかの「インデックスペア」配列ペアを指定します。pairs[i] = [a、b]は、2つのインデックス(番号は0から始まります)を意味します。 。

インデックスの任意のペアの文字を、ペアで何度でも交換できます

sが数回の交換後になり得る最小の文字列を辞書式に返します。

示例 1:

输入:s = "dcab", pairs = [[0,3],[1,2]]
输出:"bacd"

解释: 
交换 s[0] 和 s[3], s = "bcad"
交换 s[1] 和 s[2], s = "bacd"

示例 2:

输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]]
输出:"abcd"

解释:
交换 s[0] 和 s[3], s = "bcad"
交换 s[0] 和 s[2], s = "acbd"
交换 s[1] 和 s[2], s = "abcd"

回答コード:

class UnionFind:
    def __init__(self, nums):
        self.par = list(range(len(nums)))
        self.rank = [0] * len(nums)
    
    # 查询树的根   
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            return self.find(self.par[x])
        
    # 查询x个y所属集合   
    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
    
    # 判断x和y是否为同一集合
    def same(self, x, y):
        return self.find(x) == self.find(y)

class Solution:
    def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
        uf = UnionFind(s)
        i = 0
        # 寻找根节点
        for k, v in pairs:
            uf.unite(k, v)
        
        # 获取根节点对应的连通块集合
        dic = collections.defaultdict(list)
        for i in range(len(s)):
            root = uf.find(i)
            dic[root].append(i)
        # 对每个连通块中元素排序
        res = list(s)
        for k, v in dic.items():
            arr = [s[i] for i in v]
            arr.sort()
            for i in range(len(v)):
                res[v[i]] = arr[i]
                
        return "".join(res)

総括する

彼は血を吐き、それを整理しました、しかし私はあまりにも乱れていました。私はそれを一日中見ました。いい加減にして!

おすすめ

転載: blog.csdn.net/qq_42388742/article/details/112464016