【问题标题】:How to stop recursion after finding solution / return找到解决方案/返回后如何停止递归
【发布时间】:2021-12-14 16:37:18
【问题描述】:

我的递归函数有问题,希望在这里得到帮助。

我想写一个函数,找到两个节点之间的所有路径,找到解决方案后函数应该停止并输出第一个路径。如果该路径已经找到,该函数应该继续执行,直到找到下一条路径并再次停止并输出找到的路径。

为此,我编写了一个函数(或我打算这样做),它从起始节点开始,然后到它的邻居节点,然后到邻居节点的邻居节点等,直到到达结束节点。然后它应该输出路径。如果没有到达结束节点,它应该删除最后一个节点并继续在那里。所以是一种带回溯的DFS算法。

我的问题是函数在找到结束节点后并没有停止。我认为这是因为仍有“打开”的功能需要关闭。如果我在代码底部写“return pathfind(nknots)”而不是“pathfind(nknots)”,那么我的代码在到达结束节点时停止,但如果尚未到达结束节点,则不会继续回溯.所以在这两种情况下我都有问题。

有没有人知道我该如何解决这个问题?最好以一种我不必过多更改自己的代码/想法的方式?

def pathfind(x):                   
                        
   visited.add(x)
        
   if neighbours[x] != {}:

        for nknots in neighbours[x]:

            if nknots == n:
                
                print("final node is found")
                augpath.append(n)
                return augpath
            
            elif nknots in augpath or nknots not in neighbours or nknots in visited:
                
                print("Node already in augpath, visited or has no neighbours: ")
                continue
                        
            else:       
                augpath.append(nknots)

                pathfind(nknots)  # pathfind on new node

                visited.remove(nknots) 
                augpath.pop()

【问题讨论】:

  • 您的函数以visited.add(x) 开头,但您从未定义变量visited
  • 在某些时候你说函数应该在找到结束节点后继续,以搜索另一条路径。但后来你说“我的问题是函数在找到结束节点后并没有停止。”。我不明白。函数找到结束节点后该停止还是不停止?

标签: python recursion graph return


【解决方案1】:

如果您希望函数在遇到结束节点时立即停止,并返回路径,那么您应该使用return 语句告诉它停止:

def onepath(start, end, neighbours, path=[], visited=None):
    if visited is None:
        visited = {start}
    if start == end:
        return path + [start]
    else:
        for n in neighbours[start]:
            if n not in visited:
                visited.add(n)
                p = onepath(n, end, neighbours, path + [start], visited)
                if p:
                    return p
        return None

如果要查找所有路径,则应返回由所有递归调用找到的所有路径组成的列表:

def allpaths1(start, end, neighbours, path=[], visited=set()):
    if start == end:
        return [path + [start]]
    else:
        return [p
            for n in neighbours[start]
            if n not in visited
                for p in allpaths1(n, end, neighbours, path + [start], visited | {start})
        ]

请注意,带有双 for 的列表解析的语法有点尴尬。对于这些类型的函数,我认为最 Python 的方式是使用生成器,而不是列表:

def allpaths2(start, end, neighbours, path=[], visited=set()):
    if start == end:
        yield path + [start]
    else:
        for n in neighbours[start]:
            if n not in visited:
                yield from allpaths2(n, end, neighbours, path + [start], visited | {start})

neighbours = {1: [2, 3, 4], 2: [1, 3], 3: [1, 2, 4], 4: [1, 3]}
# 1 - 2
# | \ |
# 4 - 3

print(onepath(1, 3, neighbours))
# [1, 2, 3]

print(allpaths1(1, 3, neighbours))
# [[1, 2, 3], [1, 3], [1, 4, 3]]

print(list(allpaths2(1, 3, neighbours)))
# [[1, 2, 3], [1, 3], [1, 4, 3]]

【讨论】:

  • 谢谢你帮助我:)
  • 关于您的第一个算法“onepath”的问题。我怎么能找到另一条路?因此,如果我已经找到了一条路径,现在我想要另一条不同的路径到终端节点?但只有一个而不是全部,因为找到所有这些需要很长时间。我想一次只输出一个替代路径。例如,如果我运行“onepath”一次,它应该给我一条路径,第二次运行另一条路径(如果可用)。我可能不得不列出已经访问过的路径
  • @Sonny1993 这就是 python 生成器的伟大之处。注意allpaths2 如何使用关键字yield 而不是关键字return。试试这个:paths = allpaths2(1, 3, neighbours); print('first path:'); print(next(paths)); print('second path:'); print(next(paths)); print('third path:'); print(next(paths)); 每次调用next(paths),它都会计算下一条路径。如果只使用next 两次,它只计算前两条路径。从某种意义上说,计算是“惰性的”,即您尚未要求的路径尚未计算。
  • 哇,谢谢。那是一个非常好的工具。我必须更多地关注生成器和产量
  • @Sonny1993 我不确定哪里是阅读有关生成器的最佳位置。也许是Glossary: generatorExpressions: yield-expressions,虽然这些有点简洁。
猜你喜欢
  • 1970-01-01
  • 2022-07-12
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 2021-10-17
  • 2015-10-25
  • 1970-01-01
相关资源
最近更新 更多