所以这就是我想要做的事情:我有一个包含几个等价关系的列表:
l = [[1, 2], [2, 3], [4, 5], [6, 7], [1, 7]]
我想结合共享一个元素的集合。以下是一个示例实现:
def union(lis):
lis = [set(e) for e in lis]
res = []
while True:
for i in range(len(lis)):
a = lis[i]
if res == []:
res.append(a)
else:
pointer = 0
while pointer < len(res):
if a & res[pointer] != set([]) :
res[pointer] = res[pointer].union(a)
break
pointer +=1
if pointer == len(res):
res.append(a)
if res == lis:
break
lis,res = res,[]
return res
打印
[set([1, 2, 3, 6, 7]), set([4, 5])]
这是正确的事情,但是当等价关系太大时,它太慢了。我查找了union-find算法的描述:http://en.wikipedia.org/wiki/Disjoint-set_data_structure 但我仍然有编写Python实现的问题。
答案 0 :(得分:7)
O(n)
时间内运行的解决方案def indices_dict(lis):
d = defaultdict(list)
for i,(a,b) in enumerate(lis):
d[a].append(i)
d[b].append(i)
return d
def disjoint_indices(lis):
d = indices_dict(lis)
sets = []
while len(d):
que = set(d.popitem()[1])
ind = set()
while len(que):
ind |= que
que = set([y for i in que
for x in lis[i]
for y in d.pop(x, [])]) - ind
sets += [ind]
return sets
def disjoint_sets(lis):
return [set([x for i in s for x in lis[i]]) for s in disjoint_indices(lis)]
>>> lis = [(1,2),(2,3),(4,5),(6,7),(1,7)]
>>> indices_dict(lis)
>>> {1: [0, 4], 2: [0, 1], 3: [1], 4: [2], 5: [2], 6: [3], 7: [3, 4]})
indices_dict
提供了从等价#到lis
中的索引的映射。例如。 1
映射到0
中的索引4
和lis
。
>>> disjoint_indices(lis)
>>> [set([0,1,3,4], set([2])]
disjoint_indices
给出了一组不相交的索引。每个集合对应于等价的索引。例如。 lis[0]
和lis[3]
具有相同的等效性,但不是lis[2]
。
>>> disjoint_set(lis)
>>> [set([1, 2, 3, 6, 7]), set([4, 5])]
disjoint_set
将不相交的指数转换为适当的等价值。
O(n)
时间复杂度很难看到,但我会尝试解释。在这里,我将使用n = len(lis)
。
indices_dict
肯定会在O(n)
时间内运行,因为只有1个for-loop
disjoint_indices
是最难看的。它肯定在O(len(d))
时间运行,因为当d
为空时外部循环停止,并且内部循环在每次迭代时删除d
的元素。现在,len(d) <= 2n
d
是从等价#到索引的映射,2n
中最多只有lis
个等价#。因此,该函数在O(n)
。
disjoint_sets
。但是,您会注意到i
中的所有n
索引都可以运行lis
,而x
上的所有2n = O(n)
索引都会在2元组上运行,因此总复杂度为{{} 1}}
答案 1 :(得分:1)
我认为这是一个优雅的解决方案,使用内置的设置功能:
#!/usr/bin/python3
def union_find(lis):
lis = map(set, lis)
unions = []
for item in lis:
temp = []
for s in unions:
if not s.isdisjoint(item):
item = s.union(item)
else:
temp.append(s)
temp.append(item)
unions = temp
return unions
if __name__ == '__main__':
l = [[1, 2], [2, 3], [4, 5], [6, 7], [1, 7]]
print(union_find(l))
返回集合列表。
答案 2 :(得分:0)
也许是这样的?
#!/usr/local/cpython-3.3/bin/python
import copy
import pprint
import collections
def union(list_):
dict_ = collections.defaultdict(set)
for sublist in list_:
dict_[sublist[0]].add(sublist[1])
dict_[sublist[1]].add(sublist[0])
change_made = True
while change_made:
change_made = False
for key, values in dict_.items():
for value in copy.copy(values):
for element in dict_[value]:
if element not in dict_[key]:
dict_[key].add(element)
change_made = True
return dict_
list_ = [ [1, 2], [2, 3], [4, 5], [6, 7], [1, 7] ]
pprint.pprint(union(list_))
答案 3 :(得分:0)
这可以通过一次完全耗尽一个等价物来实现。当元素找到它的等价时,它将从原始集中删除,不再搜索。
def equiv_sets(lis):
s = set(lis)
sets = []
#loop while there are still items in original set
while len(s):
s1 = set(s.pop())
length = 0
#loop while there are still equivalences to s1
while( len(s1) != length):
length = len(s1)
for v in list(s):
if v[0] in s1 or v[1] in s1:
s1 |= set(v)
s -= set([v])
sets += [s1]
return sets
print equiv_sets([(1,2),(2,3),(4,5),(6,7),(1,7)])
输出: [设置([1,2,3,6,7]),设置([4,5])]