- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
摘自 here我们得到了一个最小的迭代 dfs 例程,我称它为最小是因为你很难进一步简化代码:
def iterative_dfs(graph, start, path=[]):
q = [start]
while q:
v = q.pop(0)
if v not in path:
path = path + [v]
q = graph[v] + q
return path
graph = {
'a': ['b', 'c'],
'b': ['d'],
'c': ['d'],
'd': ['e'],
'e': []
}
print(iterative_dfs(graph, 'a'))
这是我的问题,您如何将这个例程转换为拓扑排序方法,其中例程也变得“最小”?我看过 video而且这个想法非常聪明,所以我想知道是否可以将相同的技巧应用到上面的代码中,这样 topological_sort 的最终结果也变得“最小”。
不要求拓扑排序的版本不是对上述例程的微小修改,我已经看过其中的一些。问题不是“我如何在 python 中实现拓扑排序”,而是找到上述代码的最小可能调整集以成为 topological_sort
。
补充意见
作者在原文中说:
A while ago, I read a graph implementation by Guido van Rossen that was deceptively simple. Now, I insist on a pure python minimal system with the least complexity. The idea is to be able to explore the algorithm. Later, you can refine and optimize the code but you will probably want to do this in a compiled language.
这个问题的目标不是优化 iterative_dfs
而是想出一个从它派生的最小版本的拓扑排序(只是为了了解更多关于图论算法的信息)。事实上,我想一个更普遍的问题可能是给定一组最小算法,{iterative_dfs
, recursive_dfs
, iterative_bfs
, recursive_dfs
},它们的拓扑排序推导是什么?虽然这会使问题变得更冗长/复杂,但从 iterative_dfs 中找出 topological_sort 就足够了。
最佳答案
将 DFS 的迭代实现转变为拓扑排序并不容易,因为需要进行的更改使用递归实现更自然。但是你仍然可以这样做,它只需要你实现自己的堆栈。
首先,这是您的代码稍微改进的版本(它更高效,也不是更复杂):
def iterative_dfs_improved(graph, start):
seen = set() # efficient set to look up nodes in
path = [] # there was no good reason for this to be an argument in your code
q = [start]
while q:
v = q.pop() # no reason not to pop from the end, where it's fast
if v not in seen:
seen.add(v)
path.append(v)
q.extend(graph[v]) # this will add the nodes in a slightly different order
# if you want the same order, use reversed(graph[v])
return path
以下是我将如何修改该代码以进行拓扑排序:
def iterative_topological_sort(graph, start):
seen = set()
stack = [] # path variable is gone, stack and order are new
order = [] # order will be in reverse order at first
q = [start]
while q:
v = q.pop()
if v not in seen:
seen.add(v) # no need to append to path any more
q.extend(graph[v])
while stack and v not in graph[stack[-1]]: # new stuff here!
order.append(stack.pop())
stack.append(v)
return stack + order[::-1] # new return value!
我用“这里的新东西”评论的部分是在您向上移动时计算顺序的部分。它检查找到的新节点是否是前一个节点(位于堆栈顶部)的子节点。如果不是,它会弹出堆栈顶部并将值添加到 order
.在我们进行 DFS 时,order
从最后一个值开始,将采用反向拓扑顺序。我们在函数的末尾反转它,并将它与堆栈上的剩余值连接起来(方便地,它们已经按正确的顺序排列了)。
因为这段代码需要检查v not in graph[stack[-1]]
很多时候,如果 graph
中的值会更有效字典是集合,而不是列表。图通常不关心其边的保存顺序,因此进行此类更改不会导致大多数其他算法出现问题,尽管生成或更新图的代码可能需要修复。如果您打算扩展您的图形代码以支持加权图形,您可能最终将列表更改为字典,从节点映射到权重,这对于此代码同样有效(字典查找是 O(1)
只是像集合查找)。或者,我们可以自己构建我们需要的集合,如果 graph
不能直接修改。
作为引用,这里是 DFS 的递归版本,并对其进行了修改以进行拓扑排序。所需的修改确实非常小:
def recursive_dfs(graph, node):
result = []
seen = set()
def recursive_helper(node):
for neighbor in graph[node]:
if neighbor not in seen:
result.append(neighbor) # this line will be replaced below
seen.add(neighbor)
recursive_helper(neighbor)
recursive_helper(node)
return result
def recursive_topological_sort(graph, node):
result = []
seen = set()
def recursive_helper(node):
for neighbor in graph[node]:
if neighbor not in seen:
seen.add(neighbor)
recursive_helper(neighbor)
result.insert(0, node) # this line replaces the result.append line
recursive_helper(node)
return result
就是这样!一条线被删除,类似的一条被添加到不同的位置。如果您关心性能,您可能应该这样做 result.append
在第二个辅助函数中,执行 return result[::-1]
在顶层 recursive_topological_sort
功能。但是使用 insert(0, ...)
是一个更小的变化。
另外值得注意的是,如果您想要整个图的拓扑顺序,则不需要指定起始节点。实际上,可能没有一个节点可以让您遍历整个图形,因此您可能需要进行多次遍历才能到达所有内容。在迭代拓扑排序中实现这一点的一个简单方法是初始化 q
至list(graph)
(所有图键的列表)而不是只有一个起始节点的列表。对于递归版本,将调用替换为 recursive_helper(node)
如果它还没有在 seen
中,则使用循环调用图中每个节点上的辅助函数.
关于python - python中拓扑排序的看似简单的实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47192626/
我在将两个“相同”字符串转换为大写时遇到了一个非常奇怪的问题。该程序正在从网站读取行并将其与存储在文本文件中的行进行比较。如果未找到该行,则将其添加到文件末尾。除非该行包含特殊字符,否则这非常有效。由
我见过对象创建的各种模式,但在深入研究 Firefox 框架 Javascript 后,我注意到他们使用的是我以前从未见过的约定,我希望这里有人可以确认我的假设或纠正我: 在构建类时,我过去曾这样
多亏了我在 Oxfam 书店找到的一本小书和一本大书,我一直在研究 C、C++ 和 Allegro。我现在理解得很好,但我遇到了困难……每当我编译时,我都会遇到这些错误: archiboldian@a
也许标题可以改写得稍微好一点,但基本上我想知道 facebook 之类的公司如何实现关于新通知/消息等的“实时”界面。我知道这样一个社交网络背后的复杂性太在这个小的 SO 线程中有很多讨论,但如果有人
我想使用 R 的 C 接口(interface)编写一个 R 函数,该函数采用 2 列递增的非重叠整数间隔矩阵,并返回一个包含这些间隔加上一些附加间隔的列表,这样就没有间隙。例如,它应该取矩阵 rbi
我们的崩溃日志系统显示崩溃,我不明白它是如何发生的。用户输入未知。我已添加日志记录,但结果只有在下一个版本(约 2 周)后才能看到。 下面的代码如何抛出下一个异常: Crashes with java
我正在java1.7下使用JavaFX进行编程,除了这部分之外,所有内容都适用于我的代码。问题是,只有最终结果被写出来。当程序运行时,我希望它在标签中显示“Ping 测试正在运行”文本。但它不会这样做
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我有一个作用域枚举: enum class E { A, B, C }; 现在我想要一个函数,它接受该作用域 int 的值或 int 本身。 应该是这样的: template ::value, int
我有以下 Python 脚本: x = 300000000.0 while (x < x + x): x = x + x print "exec: " + str(x) print "
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: how does do{} while(0) work in macro? 示例来自 this博文: #de
我的问题是:“语言设计者为什么要考虑允许在不同数据类型之间进行比较?”。另外,这在功能语言中是否更有意义? 例如,在erlang中,可以执行以下比较: %% Tuples are greater th
我在玩 GHCI,遇到了这个(对我来说)奇怪的小东西。 我试过这个: λ> let fibs = 1 : 1 : zipWith (+) fibs (tail fibs) λ> fibs 只是为了看看
我们正在 Fixtures 的帮助下为我们的 Grails 2.0.0 应用程序编写集成测试。和 Buid-Test-Data插件。 在测试过程中,发现集成测试有时会失败,有时会通过。运行“test-
我目前有一个脚本应该获取并返回 Bit.ly 链接的点击次数。我首先从 Bitly url 收集和读取数据,我似乎在做正确的事情。 bitly_data = "https://api-ssl.
我正在为我的老师测试一些东西,他想看看如果我们模拟同步,下面的程序如何运行得更快(我知道它不可能完全同步,这只是为了实验学习/练习)多只 turtle 的运动。我尝试过使用诸如多处理、线程之类的模块,
所以我在一个输出大图像(从 30MB 到 2GB+ 的任何地方)的设备上工作。在我们开始创建这些图像之一之前,我们通过 GetDiskFreeSpaceEx 检查是否有足够的磁盘空间。通常(在这种情况
我正在尝试通过深入了解操作系统的底层细节来学习操作系统。我现在上的类(class)是MIT 6.828 Operating System Engineering 。该实验室要求学生追踪 BIOS 以获
我知道你不能在 Lua 中重复匹配组。例如,如果我想匹配两个连续的 "45",我不能这样做: print(string.find("some 4545 text", "(%d%d)+")) 这将打印
这是我创建的一个 plunker:http://plnkr.co/edit/ZoKsO7wu5OvCYtwEi9Iy?p=preview . 点击列表中使用 ng-repeat 渲染的项目之一,例如
我是一名优秀的程序员,十分优秀!