【问题标题】:Find round routes in a list of tuples在元组列表中查找循环路线
【发布时间】:2021-07-21 11:22:01
【问题描述】:

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

例如:

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

但是

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

不行,我们可以从 1 → 2 → 3 → 1

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

有什么想法可以实现吗?

【问题讨论】:

  • 你试过什么?你需要建立一个图(这只是一个字典),然后你需要做一个深度优先的树遍历,看看你是否回到任何以前访问过的节点。
  • 在计算机科学术语中,您试图证明您的“有向图”是“有向无环图”。

标签: python python-3.x data-structures computer-science


【解决方案1】:

这是一个运行良好的 Python 实现(也适用于 Java 和 C++,但我只尝试了第一种情况)。 https://www.techiedelight.com/check-given-digraph-dag-directed-acyclic-graph-not/

【讨论】:

    【解决方案2】:

    如果我理解正确,那么问题是什么时候可以到达起点。为了简单和明确起见,我将从一个普通的 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, all_src, 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)],
            [(1, 2), (3, 4), (2, 3)],
            [(1, 2), (3, 4), (2, 3), (3, 1)],
            [(5, 6), (5, 7), (5, 8), (5, 9), (9, 10), (10, 5)],
            [(5, 6), (5, 7), (5, 8), (5, 9), (9, 10), (10, 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
    

    【讨论】:

      【解决方案3】:

      这有点晚了,但提供了一个更短的解决方案(更少的 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,2), (3,4), (2,3)])
      False
      >>> lst_loops([(1,2), (3,4), (2,3), (3,1)])
      True
      >>> 
      

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

      【讨论】:

        【解决方案4】:

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

        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, 2)], [(1, 2), (2, 3)], [(1, 2), (3, 4), (2, 3)], [(1, 2), (3, 4), (2, 3), (3, 1)], [(5, 6), (5, 7), (5, 8), (5, 9), (9, 10), (10, 5)], [(5, 6), (5, 7), (5, 8), (5, 9), (9, 10), (10, 15)]]
        result = [no_cycles(i) for i in graphs]
        

        输出:

        [True, True, True, False, False, True]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-01-22
          • 2011-01-12
          • 2016-07-18
          • 1970-01-01
          • 2015-07-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多