Algorithm 如何用链表实现不相交集数据结构&xFF1F;

Algorithm 如何用链表实现不相交集数据结构&xFF1F;,algorithm,data-structures,Algorithm,Data Structures,我不知道如何用链表实现不相交的集合数据结构,谁能告诉我 我对find-set()如何在这样的实现中工作O(1)次感到特别困惑。thx ~不相交的集合数据结构并不能在每个操作的O(1)次内工作。相反,它在大量操作中以O(1)时间工作 每个节点都存储一个指向其父节点的指针,每个不相交集的代表元素是尽可能多地跟随父节点指针的节点。要获得O(1),必须折叠父节点链,以便在跟随父链接时每个元素都指向头节点 这里有一些Python代码可以完成所有这些。请特别注意head方法中的第二个while循环:这是使数

我不知道如何用链表实现不相交的集合数据结构,谁能告诉我
我对find-set()如何在这样的实现中工作O(1)次感到特别困惑。thx ~

不相交的集合数据结构并不能在每个操作的O(1)次内工作。相反,它在大量操作中以O(1)时间工作

每个节点都存储一个指向其父节点的指针,每个不相交集的代表元素是尽可能多地跟随父节点指针的节点。要获得O(1),必须折叠父节点链,以便在跟随父链接时每个元素都指向头节点

这里有一些Python代码可以完成所有这些。请特别注意
head
方法中的第二个while循环:这是使数据结构高效工作的关键

class Node:
    def __init__(self, value):
        self.value = value
        self.parent = None

    def head(self):
        head = self
        while head.parent:
            head = head.parent
        while self != head:
            self.parent, self = head, self.parent
        return head

    def union(a, b):
        if a.head() != b.head():
            a.head().parent = b.head()


nodes = [Node(i) for i in range(10)]
nodes[0].union(nodes[1])
nodes[0].union(nodes[2])

print set(node.head().value for node in nodes)

虽然不相交集数据结构通常被解释为一个树森林,但它通常是通过数组实现的:一个数组将索引映射到其父索引,另一个数组将索引映射到其秩。这就消除了很多无意义的开销(在空间和时间上),这在理论上是不相关的,但在实践中是相关的。您可能需要一种在某种对象的索引和实例之间映射的方法

顺便说一下,
find
在O(1)中不起作用。使用列组的技巧可以防止
find
使用线性时间,但在最坏的情况下,它仍然可以使用对数步数。O(α(n))时间是一个摊销时间,很难解释它从何而来——你可以从一个好的但不是线性集并算法[Tarjan]的效率中找到分析

以下是一种可行的方法:

disjoint_set {
    int[] parent, rank;
    makeset(int n)
    {
        rank = new int[n];
        parent = new int[n];
        for(int i = 0; i < n; i++)
            parent[i] = i;
    }

    int find(int i)
    {
        if (parent[i] != i)
            parent[i] = find(parent[i]);
        return parent[i];
    }

    void union(int x, int y)
    {
        x_root = find(x);
        y_root = find(y);
        if (x_root != y_root) {
            if (rank[x_root] < rank[y_root])
                parent[x_root] = y_root;
            else if (rank[x_root] > rank[y_root])
                parent[y_root] = x_root;
            else {
                parent[y_root] = x_root;
                rank[x_root]++;
            }
        }
    }
}
不相交集{
int[]父级,秩;
生成集(int n)
{
秩=新整数[n];
父项=新整数[n];
对于(int i=0;i秩[y_根])
父[y_根]=x_根;
否则{
父[y_根]=x_根;
秩[x_根]+;
}
}
}
}

为什么要使用链表实现此功能?通常这是通过树状结构实现的。这可能需要一些路径compression@chill它已经有了,注意
parent[i]=find(parent[i])使用数组而不是树状结构确实是个好主意!“这消除了很多无意义的开销(在空间和时间上),这在理论上是不相关的,但在实践中是如此。”-基于节点(每个节点存储秩和指向其父节点的指针)的方法意味着什么样的开销?实际上,数据的空间位置会更好——缓存行为会更糟,th.@ PascMell在标签上运行,我认为你在思考C++,而我不是(例如,java/C所有对象都有几个隐藏的额外字段)。不过,现在指针通常比int大,然后对齐将节点的大小提升到16字节(+已经增加100%),动态分配大量节点也会带来大量开销(时间和空间)<代码>向量
vs两个int向量将是一个有趣的实验