そしてルックアップ
ユニオン検索セットはツリー型のデータ構造であり、一部のばらばらなセットのマージおよびクエリの問題に対処するために使用されます。2 つの主な操作 (マージとクエリ) があります。
- 2 つの要素間の関係に基づいて 2 つのコレクションをマージします。
- 2 つの要素間に関係があるかどうか (同じコレクション内にあるかどうか) とクエリ ノードのルート ノードをクエリします。
達成:
セットがツリーを構築し、要素がセットのルート ノードとして選択されます。
各要素の親ノードを記録する pre 値を作成します。pre[現在のノード] は親ノードであり、ルート ノードの親ノードはそれ自体です。
1.マージ
要素の関係を考慮して、あるセットのツリーを別のセットの子に変更します (あるセットのルート ノードの親ノードを別のセットのルート ノードに変更します)。例: B のルート ノードは、 A のルート ノード、これ 2 つのツリー A と B を 1 つのツリーにマージできます。
2. クエリ
2 つの要素が同じコレクションにあるかどうかを確認します。
この要素から親ノードへのアクセスを開始し (通常は再帰検索を使用)、ルート ノードが段階的にアクセスされるまで、2 つの要素のルート ノードを比較して判断します (2 つの要素のルート ノードが同じ場合)の場合、それらは同じセットに属し、そうでない場合は同じセットに属しません)。
注: ユニオン検索の「ユニオン」と「検索」の時間計算量は、ツリーの深さに依存します。
パスの圧縮 (最適化)
一部のツリー構造 (左側に示すように) は、基本的に時間計算量を最適化できません。したがって、アルゴリズム最適化 - パス圧縮 (右図参照) を使用する必要があります。
クエリ操作では、最終的な目標は、この要素が配置されているツリーのルート ノードを見つけることです。そのため、他の要素とどのように関連しているかは、私たちには意味がありません。つまり、多くの場合、クエリ プロセス中に関係するのはルート ノードのみです (ツリーの形状には関係ありません)。そのため、アクセスした各ノードは、クエリ中にツリーのルート ノードを指すことができます (パス)。圧縮)。したがって、各クエリでは、クエリ対象の要素によってルート ノードに渡されたノードに対して 1 ステップのパス圧縮が実行されます。渡されたノードの親ノードをルート ノードに変更し、pre[渡されたノード] はルート ノードに等しくなります。ノード。
パイソンコード
"""并查集"""
import numpy as np
class UnionFindSet():
def __init__(self, data_list):
# 初始化两个字典,分别保存节点的父节点(并查集)和保存父节点的大小
self.father_dict = {} # father_dict[i]表示节点i的父节点
self.size_dict = {} # size_dict[i]表示节点i的后代节点个数
# 初始化节点,将节点的父节点设为自身,size设为1
for node in data_list:
self.father_dict[node] = node
self.size_dict[node] = 1
# 递归查找根节点(父节点是自己的节点)
def find(self, node):
# 获取节点的父节点
father = self.father_dict[node]
# 查找当前节点的父节点,直到父节点是其自己
if(node != father):
# 在降低树高优化时,确保父节点大小字典正确
if father != self.father_dict[father]:
self.size_dict[father] -= 1
# 递归查找节点的父节点,直到根节点
father = self.find(father)
# 在查找父节点的时候,顺便把当前节点移动到父节点上面(优化操作)
self.father_dict[node] = father
return father
# 查看两个节点是不是在一个集合里面
def is_same_set(self, node_a, node_b):
# 获取两个节点的父节点并比较父节点是否是同一个
return self.find(node_a) == self.find(node_b)
# 将两个集合合并在一起(只需合并根节点),size_dict大吃小(尽可能降低树高)
def union(self, node_a, node_b):
if node_a is None or node_b is None:
return
# 找到两个节点各自的根节点
a_root = self.find(node_a)
b_root = self.find(node_b)
# 两个节点不在同一集合中,则合并两个集合
if(a_root != b_root):
# 获取两个集合根节点的大小
a_set_size = self.size_dict[a_root]
b_set_size = self.size_dict[b_root]
# 判断两个集合根节点大小,并进行合并(大吃小)
if(a_set_size >= b_set_size):
# 合并集合
self.father_dict[b_root] = a_root
# 更新大小
self.size_dict[a_root] = a_set_size + b_set_size
else:
# 合并集合
self.father_dict[a_root] = b_root
# 更新大小
self.size_dict[b_root] = a_set_size + b_set_size
if __name__=="__main__":
# 待合并节点对(关系)
node_union = [[1, 2], [1, 5], [3, 4], [5, 2], [1, 3]]
# 待判断的节点对(关系)
check_node = [[1, 4], [2, 3], [5, 6]]
# 所有节点
setN = set(np.array(node_union).flatten().tolist()+np.array(check_node).flatten().tolist())
# 并查集
model = UnionFindSet(list(setN))
# 循环合并节点
print("开始合并----------")
for i in range(len(node_union)):
node1, node2 = node_union[i][0], node_union[i][1]
model.union(node1, node2)
# 循环查找父节点
print("开始查找----------")
for i in setN:
father = model.find(i)
print("节点{}的根节点是{}".format(i, father))
# 循环判断关系(是否属于同一集合)
print("开始判断----------")
for i in range(len(check_node)):
node1, node2 = check_node[i][0], check_node[i][1]
if model.is_same_set(node1, node2):
print('{}和{}属于同一集合'.format(node1, node2))
else:
print('{}和{}属于不同集合'.format(node1, node2))