链接列表搜索在它们发生之前停止循环

时间:2016-06-03 12:04:27

标签: python

我有一系列帧,每帧可以是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))

两者都不起作用,但由于搜索的性质(我之前设法做了一个正常的双链接),我不太确定哪个部分需要修复。如果有人可以提供像第一个这样的东西,它会跑得更快,那就太棒了,否则任何有效的东西都会很好,我花了好几个小时试图弄清楚这一点没有运气。

2 个答案:

答案 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将返回从该节点可到达的所有节点的列表。