これは、依存関係のないかなり高速なソリューションです。次のように機能します。
各生存者に一意の参照番号を割り当てます(この場合、サブリストの初期インデックス)
各サブリスト、および各サブリストの各アイテムの参照要素のディクショナリを作成します。
変更がなくなるまで、次の手順を繰り返します。
3a。各サブリストの各項目に目を通します。そのアイテムの現在の参照番号がそのサブリストの参照番号と異なる場合、要素は2つのリストの一部である必要があります。2つのリストをマージし(現在のサブリストを参照から削除します)、現在のサブリスト内のすべてのアイテムの参照番号を新しいサブリストの参照番号に設定します。
この手順で変更が発生しない場合は、すべての要素が1つのリストの一部であるためです。ワーキングセットのサイズは反復ごとに減少するため、アルゴリズムは必然的に終了します。
def merge_overlapping_sublists(lst):
output, refs = {}, {}
for index, sublist in enumerate(lst):
output[index] = set(sublist)
for elem in sublist:
refs[elem] = index
changes = True
while changes:
changes = False
for ref_num, sublist in list(output.items()):
for elem in sublist:
current_ref_num = refs[elem]
if current_ref_num != ref_num:
changes = True
output[current_ref_num] |= sublist
for elem2 in sublist:
refs[elem2] = current_ref_num
output.pop(ref_num)
break
return list(output.values())
このコードの一連のテストは次のとおりです。
def compare(a, b):
a = list(b)
try:
for elem in a:
b.remove(elem)
except ValueError:
return False
return not b
import random
lst = [["a", "b"], ["b", "c"], ["c", "d"], ["d", "e"]]
random.shuffle(lst)
assert compare(merge_overlapping_sublists(lst), [{"a", "b", "c", "d", "e"}])
lst = [["a", "b"], ["b", "c"], ["f", "d"], ["d", "e"]]
random.shuffle(lst)
assert compare(merge_overlapping_sublists(lst), [{"a", "b", "c",}, {"d", "e", "f"}])
lst = [["a", "b"], ["k", "c"], ["f", "g"], ["d", "e"]]
random.shuffle(lst)
assert compare(merge_overlapping_sublists(lst), [{"a", "b"}, {"k", "c"}, {"f", "g"}, {"d", "e"}])
lst = [["a", "b", "c"], ["b", "d", "e"], ["k"], ["o", "p"], ["e", "f"], ["p", "a"], ["d", "g"]]
random.shuffle(lst)
assert compare(merge_overlapping_sublists(lst), [{"k"}, {"a", "c", "b", "e", "d", "g", "f", "o", "p"}])
lst = [["a", "b"], ["b", "c"], ["a"], ["a"], ["b"]]
random.shuffle(lst)
assert compare(merge_overlapping_sublists(lst), [{"a", "b", "c"}])
戻り値はセットのリストであることに注意してください。