您可以使用一种数据结构使合并操作更加高效。在此过程中,您需要创建某种相反的树形结构。因此,在您的示例中,您首先将创建列出的数字:
1 2 3 4 5 8 10
现在,如果您遍历元组
(1,2)
,则会在某种字典中查找
1
和
2
。您搜索它们的祖先(这里没有),然后创建某种
合并节点:
1 2 3 4 5 8 10
\/
12
下一步,我们合并(1,3),因此我们查找1的祖先(12)和3的祖先(3),然后执行另一个合并操作:
1 2 3 4 5 8 10
\/ |
12 /
\/
123
下一步我们合并
(2,4)
、
(5,8)
和
(8,10)
:
1 2 3 4 5 8 10
\/ | | \/ |
12 / | 58 /
\/ / \/
123 / 5810
\/
1234
您还需要维护一个“合并头”列表,以便轻松返回元素。
开始动手
现在我们知道如何构建这样的数据结构了,让我们来实现它。首先,我们定义一个节点:
class Merge:
def __init__(self,value=None,parent=None,subs=()):
self.value = value
self.parent = parent
self.subs = subs
def get_ancestor(self):
cur = self
while cur.parent is not None:
cur = cur.parent
return cur
def __iter__(self):
if self.value is not None:
yield self.value
elif self.subs:
for sub in self.subs:
for val in sub:
yield val
现在我们首先为列表中的每个元素初始化一个字典:
vals = set(x for tup in array for x in tup)
为vals
中的每个元素创建一个字典,将其映射到Merge
:
dic = {val:Merge(val) for val in vals}
还有 merge_heads
:
merge_heads = set(dic.values())
现在对于数组中的每个元组,我们查找相应的祖先Merge
对象,我们在其上面创建一个新的Merge
,从merge_head
集合中删除两个旧的头,并将新的merge
添加到其中:
for frm,to in array:
mra = dic[frm].get_ancestor()
mrb = dic[to].get_ancestor()
mr = Merge(subs=(mra,mrb))
mra.parent = mr
mrb.parent = mr
merge_heads.remove(mra)
merge_heads.remove(mrb)
merge_heads.add(mr)
最后,在完成这一步骤后,我们可以简单地为merge_heads
中的每个Merge
构建一个set
:
resulting_sets = [set(merge) for merge in merge_heads]
然后resulting_sets
将会是(顺序可能不同):
[{1, 2, 3, 4}, {8, 10, 5}]
将所有内容组合在一起(不包括
class
定义):
vals = set(x for tup in array for x in tup)
dic = {val:Merge(val) for val in vals}
merge_heads = set(dic.values())
for frm,to in array:
mra = dic[frm].get_ancestor()
mrb = dic[to].get_ancestor()
mr = Merge(subs=(mra,mrb))
mra.parent = mr
mrb.parent = mr
merge_heads.remove(mra)
merge_heads.remove(mrb)
merge_heads.add(mr)
resulting_sets = [set(merge) for merge in merge_heads]
这将最坏情况下以O(n2)运行,但您可以使树平衡,使祖先在O(log n)中找到,从而使其变为O(n log n)。此外,您还可以短路祖先列表,使其更快。