素集合研究ノート(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 ]=x、xxの場合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)
総括する
彼は血を吐き、それを整理しました、しかし私はあまりにも乱れていました。私はそれを一日中見ました。いい加減にして!