我有一系列帧,每帧可以是None
或指向另一帧。我不希望任何循环回到自身,所以我想快速检查哪些帧可用于某个帧指向。
例如,如果第1帧指向2,第2帧指向3,那么我想知道第3帧不能指向1或2,因为它会导致循环。如果第3帧指向4,则4仍然有效。
举个例子,把指针作为:
frame_pointers = {5: 6,
6: 7,
7: 8,
8: None,
9: 10,
10: None,
11: 9,
12: 6}
以下是每个框架可以指出的预期结果:
5: [6, 7, 8, 9, 10, 11, 12]
6: [7, 8, 9, 10, 11] (since 5 and 12 are pointing at it)
7: [8, 9, 10, 11] (since 6 is pointing at it and 5 and 12 are at 6)
8: [9, 10, 11] (since 7 is pointing at it and 6 at 7 etc)
9: [5, 6, 7, 8, 10, 12]
10: [5, 6, 7, 8, 12]
11: [5, 6, 7, 8, 9, 10, 12]
12: [5, 6, 7, 8, 9, 10, 11]
基本上,我只需要检查指向它的是什么,或指向指向它的任何其他东西。
这是我的第一次尝试,使用递归迭代所有值:
def get_valid_links(pointers, current_frame):
current_value = pointers[current_frame]
frames = []
for j in sorted(pointers.keys()):
#Make sure there will be no clashes
if j != current_value:
#Ignore value if it matches the selected frame
if j == current_frame:
continue
value = pointers[j]
#Ignore value if it has an immediate link to the current frame
if value == current_frame:
continue
elif value is not None:
#Check if it has any links
if recursive_search(j):
continue
frames.append(j)
return frames
def recursive_search(frame):
#Similar function to above
for j in sorted(frame_pointers.keys()):
value = frame_pointers[j]
if value == frame:
return True
elif value is not None:
if recursive_search(value):
return True
return False
for i in sorted(frame_pointers.keys()):
print 'Frames accessible to {}: {}'.format(i, ', '.join(map(str, get_valid_links(frame_pointers, i))))
这是我的第二次尝试,使用节点类来简化搜索:
class Node:
def __init__(self, c=None, p=None):
self.c = c
self.p = p if p else []
def __repr__(self):
return 'Node(c={x.c}, p={x.p})'.format(x=self)
def build_linked_list(pointers):
"""Create list of Nodes"""
new_list = {}
sorted_keys = sorted(frame_pointers.keys())
for k in sorted_keys:
parents = []
for j in sorted_keys:
if frame_pointers[j] == k:
parents.append(j)
new_list[k] = Node(c=frame_pointers[k], p=parents)
return new_list
def recursive_search(start_frame, end_frame, linked_list, visited_nodes=None):
"""Twin linked list type search"""
if visited_nodes is None:
visited_nodes = [start_frame]
else:
visited_nodes = list(visited_nodes) + [start_frame]
frame = linked_list[start_frame]
if start_frame == end_frame:
return True
if frame.c is not None and frame.c not in visited_nodes:
if recursive_search(frame.c, end_frame, linked_list, visited_nodes):
return True
for j in frame.p:
if j == end_frame:
return True
if j is not None and j not in visited_nodes:
if recursive_search(j, end_frame, linked_list, visited_nodes):
return True
def recursive_wrapper(start_frame, linked_list):
all_values = [start_frame]
for j in linked_list[start_frame].p:
if recursive_search(j, start_frame, linked_list):
all_values.append(j)
return [i for i in linked_list.keys() if i not in all_values]
for i in frame_pointers:
print recursive_wrapper(i, build_linked_list(frame_pointers))
两者都不起作用,但由于搜索的性质(我之前设法做了一个正常的双链接),我不太确定哪个部分需要修复。如果有人可以提供像第一个这样的东西,它会跑得更快,那就太棒了,否则任何有效的东西都会很好,我花了好几个小时试图弄清楚这一点没有运气。
答案 0 :(得分:2)
鉴于每个帧只能指向另一个帧,对于每个帧,一个简单的方法是构建后面的帧序列。然后,我们可以检查给定帧是否在该序列中。如果是这样,那么让给定的帧指向序列的初始帧将创建一个循环。
def get_valid_links(pointers, current_frame):
allowed_frames = []
for frame in pointers:
if frame == current_frame:
continue
sequence = []
next_frame = pointers[frame]
while next_frame:
sequence.append(next_frame)
next_frame = pointers[next_frame]
if current_frame not in sequence:
allowed_frames.append(frame)
return allowed_frames
我不确定对于大量的帧,性能会是什么样的。但是,也许你可以缓存结果等等。
如果一个帧可以指向几个帧,那么我们将使用有向图结束graph theory。我们要寻找的是一种在该图中找到周期(或潜在周期)的方法。不幸的是,我不是这方面的专家,但我建议要么看这个blog post(因为它是由Python的创建者Guido van Rossum编写的,我会认为这是一个好的开始) ,或者拿出重枪并获得完整的graph library。
答案 1 :(得分:1)
正如另一位回答者所说,你正在处理有向无环图。对于每个节点,您希望找到该节点的所有父节点的集合(该节点可从其到达的所有节点),这相当于查找该节点的所有子节点的集合(所有节点)可以从反转图中获得(通过反转图中每条边的方向获得)。我不知道你对图论有多么自在,所以如果你澄清这一点会很有帮助。 (编辑:我看到了你的评论,如果有任何需要澄清,请告诉我)
一旦你获得了反向图,你就可以使用深度优先搜索在线性时间内找到该图中节点的子集(DFS是O(| V | + | E |),其中| V |是节点数,| E |是边数,但在这里我们知道| E |< = | V |)。因此,只有DFS的基本实现,您必须在图中的每个节点上运行它,整个事物将是O(| V | ^ 2)。
可能有更优雅的方式来查找反向图,但我在下面的内容可以完成工作。这段代码需要使用稍微不同的框架表示,但我认为这很容易从上面的编写方式中获得。
from collections import defaultdict
frame_pointers = {5: set([6]),
6: set([7]),
7: set([8]),
8: set(),
9: set([10]),
10: set(),
11: set([9]),
12: set([6])}
inverse_graph = defaultdict(list)
for x, adj in frame_pointers.items():
for y in adj:
inverse_graph[y].append(x)
inverse_graph = dict(inverse_graph)
for key in frame_pointers.keys():
if inverse_graph.get(key) == None:
inverse_graph[key] = set()
else:
inverse_graph[key] = set(inverse_graph[key])
def dfs(graph, start):
visited, stack = set(), [start]
while stack:
vertex = stack.pop()
if vertex not in visited:
visited.add(vertex)
stack.extend(graph[vertex] - visited)
return visited
给定一个起始节点,DFS将返回从该节点可到达的所有节点的列表。