一种相对幼稚的二次算法
从您的路径列表中弹出一个路径。在列表中弹出另一个路径以将其连接起来。将连接的路径推回列表中。如果在任何时候我们都找不到另一条路径来连接它,这意味着答案是否定的,所有节点对都不会组合成一条路径,所以我们返回None。
def combine_into_one_path(list_of_paths):
path = list_of_paths.pop()
while list_of_paths:
path2 = pop_adjacent_path(list_of_paths, path[0], path[-1])
if path2 is None:
return None
elif path[-1] == path2[0]:
path = path[:-1] + path2
elif path2[-1] == path[0]:
path = path2[:-1] + path
else:
assert(False)
return path
def pop_adjacent_path(list_of_paths, a, b):
for i,p in enumerate(list_of_paths):
if p[0] in (a, b) or p[-1] in (a,b):
return list_of_paths.pop(i)
return None
print(combine_into_one_path([[1, 2], [5, 6], [2, 3], [3, 4], [4, 5], [0, 1]]))
# [0, 1, 2, 3, 4, 5, 6]
print(combine_into_one_path([[1, 2], [5, 6], [2, 3], [3, 7], [4, 5], [0, 1]]))
# None
该算法在路径数上是二次的,因为combine_into_one_path 中的while 循环对列表中的每个路径进行一次迭代,并且函数pop_adjacent_path 也对列表进行迭代。
请注意,此代码不会检查节点是否唯一;例如,[v1, v2, v3, v2, v4, v1, v5] 将被视为有效路径。您可以在 combine_into_one_path 的最终返回之前添加检查,以确保路径中的每个元素都是唯一的。
线性化
算法变慢的原因是必须遍历整个列表以找到一对节点来组合我们当前的路径。避免这种情况的一种方法是将配对存储在字典中,这样我们就可以回答“配对是否以a 结尾?”的问题。和“一对是否以b 开头?”在恒定的时间内。
def combine_into_one_path(list_of_paths):
path = list_of_paths.pop()
forwards = {p[0]:p for p in list_of_paths}
backwards = {p[-1]:p for p in list_of_paths}
while forwards:
if path[-1] in forwards:
p2 = forwards[path[-1]]
del forwards[path[-1]]
del backwards[p2[-1]]
path = path[:-1] + p2
elif path[0] in backwards:
p2 = backwards[path[0]]
del backwards[path[0]]
del forwards[p2[0]]
path = p2[:-1] + path
else:
return None
print('\npath =', path)
print('forwards =', forwards)
print('backwards=', backwards)
return path
print(combine_into_one_path(['manta', 'alcats', 'random']))
# randomantalcats
这几乎是相同的算法,但我们将函数 pop_adjacent_path 替换为字典检查,它是常数时间而不是线性时间。
只是为了了解算法的工作原理:
list_of_paths = [[1, 2], [5, 6], [3, 4], [4, 5], [0, 1], [2, 3]]
path = [2, 3]
forwards = {1: [1, 2], 5: [5, 6], 3: [3, 4], 4: [4, 5], 0: [0, 1]}
backwards= {2: [1, 2], 6: [5, 6], 4: [3, 4], 5: [4, 5], 1: [0, 1]}
path = [2, 3, 4]
forwards = {1: [1, 2], 5: [5, 6], 4: [4, 5], 0: [0, 1]}
backwards= {2: [1, 2], 6: [5, 6], 5: [4, 5], 1: [0, 1]}
path = [2, 3, 4, 5]
forwards = {1: [1, 2], 5: [5, 6], 0: [0, 1]}
backwards= {2: [1, 2], 6: [5, 6], 1: [0, 1]}
path = [2, 3, 4, 5, 6]
forwards = {1: [1, 2], 0: [0, 1]}
backwards= {2: [1, 2], 1: [0, 1]}
path = [1, 2, 3, 4, 5, 6]
forwards = {0: [0, 1]}
backwards= {1: [0, 1]}
path = [0, 1, 2, 3, 4, 5, 6]
forwards = {}
backwards= {}