在元组列表中查找圆形路线

问题描述

我有一个包含源和目标的元组列表 我需要确保没有“圆形路线”

例如:

[(1,2),(3,4),(2,3)]. is OK

但是

[(1,3),1)]. 

我们可以从1 → 2 → 3 → 1

我怀旧了,想起了我的计算机科学学位,所以我想到了图数据结构。不幸的是,我无法使用 python 找到一个好的实现,我在谷歌上搜索(以及在 stackoverflow 上)找到的所有示例都只是为了找到最短路径。

有什么想法可以实现吗?

解决方法

这里有一个 Python 实现,效果很好(也可以在 Java 和 C++ 中使用,但我只尝试了第一种情况)。 https://www.techiedelight.com/check-given-digraph-dag-directed-acyclic-graph-not/

,

如果我理解正确,那么问题在于您何时可以到达起点。为了简单和明确,我将从一个普通的python开始。显而易见的蛮力解决方案是检查数据中每个来源的所有可能目的地。当然,您需要稍微组织一下数据。那么它只是一个链接。下面的代码实现了这种方法。

def get_all_sources(data):

    ans = dict()
    for src,dst in data:
        ans.setdefault(src,set()).add(dst)

    return ans


def get_all_possible_destinations(src,all_src,ans=None):

    ans = set() if ans is None else ans
    for dst in all_src.get(src,set()):
        if dst in ans:
            continue
        ans.add(dst)
        get_all_possible_destinations(dst,ans)

    return ans


def pipeline_source_by_source(data):

    all_src = get_all_sources(data)

    for src in all_src:
        all_possible_destiations = get_all_possible_destinations(src,all_src)
        if src in all_possible_destiations:
            print(f"found problem: {src} -> {src}")
            break
    else:
        print('no problems found')


if __name__ == '__main__':

    data_list = [
        [(1,2)],[(1,2),(2,3)],(3,4),3),1)],[(5,6),(5,7),8),9),(9,10),(10,5)],15)]
    ]

    for idx,data in enumerate(data_list):
        print(idx)
        pipeline_source_by_source(data)

结果:

0
no problems found
1
no problems found
2
no problems found
3
found problem: 1 -> 1
4
found problem: 5 -> 5
5
no problems found
,

这有点晚了,但提供了更短的解决方案(更少的 Python 行)。其基本原理是构建一个可从源(由源索引)直接到达的目的地字典。然后从任何来源浏览可能的目的地,并将任何已经看到的目的地存储在一个集合中。一旦看到一个新的目的地,就会有一个循环。

Python 代码可以是:

def build_dict(lst):
    d = dict()
    for src,dst in lst:
        if src not in d:
            d[src] = []
        d[src].append(dst)
    return d

def dict_loops(d,start=None,seen=None):
    if start is None:
        return any(dict_loops(d,elt,None) for elt in d.keys())
    if start not in d:
        return False
    if seen is None:
        seen = {start}
    for hop in d[start]:
        if hop in seen:
            return True
        if dict_loops(d,hop,seen):
            return True
    return False

def lst_loops(lst):
    return dict_loops(build_dict(lst))

它按预期给出:

>>> lst_loops([(1,3)])
False
>>> lst_loops([(1,1)])
True
>>> 

表示第一个列表中没有循环,第二个中至少有一个。

,

您可以使用递归生成器函数:

def no_cycles(graph):
  def has_cycles(n,s = []):
     if n in s:
        yield (False,n)
     else:
        yield (True,n)
        yield from [i for a,b in graph for i in has_cycles(b,s+[n]) if a == n]
  return all(a for a,_ in has_cycles(graph[0][0]))

graphs = [[(1,15)]]
result = [no_cycles(i) for i in graphs]

输出:

[True,True,False,True]