そしてチェックセット(pythonコード実装)

そしてルックアップ

        ユニオン検索セットはツリー型のデータ構造であり、一部のばらばらなセットのマージおよびクエリの問題に対処するために使用されます。2 つの主な操作 (マージとクエリ) があります。

  1. 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))

おすすめ

転載: blog.csdn.net/qq_41750911/article/details/125104624