您可以在O(n + e)(即節點數+邊的線性)中解決此問題。
這個想法是,你首先創建一個拓撲排序(我是Tarjan's algorithm的粉絲)和一組反向邊緣。如果您可以分解問題以利用現有解決方案,它總是有幫助的。
然後,您將向後推送到每個父節點的拓撲排序其子節點距離+ 1(如果存在多個路徑,則保持最大值)。跟蹤迄今爲止所見最大距離的節點。
當你用完全的距離註釋完所有的節點後,你可以從距離最遠的節點開始,這個距離將是你最長的路徑根,然後選擇剛好小於一個數的子節點當前節點(因爲它們位於關鍵路徑上)。
一般來說,當試圖找到最佳複雜度算法時,不要害怕依次運行多個階段。五爲O(n)算法運行順序仍然是爲O(n),仍然是優於爲O(n )從複雜的角度(雖然它可能取決於不變成本會更糟實際運行時間/因素和尺寸n)。
ETA:我剛剛注意到你有一個開始節點。這使得它只是一個深度優先搜索,並保持迄今爲止所見最長的解決方案,無論如何這只是O(n + e)。遞歸是好的,或者你可以保留訪問節點的列表/堆棧(每當你回溯時找到下一個孩子時你必須小心)。
當您從深度初始搜索回溯時,您需要存儲從該節點到葉的最長路徑,以便不重新處理任何子樹。這也將用作visited
標誌(即,除了在遞歸之前做node not in path
測試還具有node not in subpath_cache
測試)。除了存儲子路徑之外,您可以存儲長度,然後基於上面討論的順序值(關鍵路徑)完成一次完成的路徑。
ETA2:下面是一個解決方案。
def find_longest_path_rec(graph, parent, cache):
maxlen = 0
for node in graph[parent]:
if node in cache:
pass
elif node not in graph:
cache[node] = 1
else:
cache[node] = find_longest_path_rec(graph, node, cache)
maxlen = max(maxlen, cache[node])
return maxlen + 1
def find_longest_path(graph, start):
cache = {}
maxlen = find_longest_path_rec(graph, start, cache)
path = [start]
for i in range(maxlen-1, 0, -1):
for node in graph[path[-1]]:
if cache[node] == i:
path.append(node)
break
else:
assert(0)
return path
請注意,我已經刪除了node not in path
測試,因爲我假設你如權利實際上是提供一個DAG。如果你想要檢查你應該提出一個錯誤,而不是忽略它。另請注意,我已將斷言添加到for
的else
子句中,以記錄我們必須始終在路徑中找到有效的下一個(順序)節點。
ETA3:最後for
循環有點混亂。我們正在考慮的是,在關鍵路徑中,所有節點距離必須是連續的。考慮節點0是距離4,節點1是距離3,節點2是距離1.如果我們的路徑開始於[0, 2, ...]
,我們有一個矛盾,因爲節點0不是離一個葉子比2更遠。
你可以運行這個通過探查器來看看性能的痛點是什麼? – Makoto
注意:[使用空列表作爲默認參數並不能滿足許多新的Python程序員最初的期望](http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-默認參數) – chucksmash
首先,修改默認參數爲chucksmash建議。其次,添加代碼來跟蹤每個訪問節點的最長已知路徑,而不僅僅是當前路徑。即使當前路徑(可能)被認爲較差,您當前的算法也會查找圖中的每條路徑。 – Prune