算法问题:在有向图中找到最长的基本循环
Algorithm Problem: Find the longest elementary cycle in a directed graph
问题是:给定一个有向图,找出图中最长的简单环。我搜索了这个问题并找到了这个 link Finding the longest cycle in a directed graph using DFS。答案说明这是一个NP难题。
但是我对下面的算法感到困惑,它似乎在 O(|V| + |E|) 时间内 运行 因为我们只访问了每条边一次。
维护以下变量:
(1) global max
:图中最长循环的长度。
(2) Map<GraphNode, Integer> cache
:存储从关键节点开始的最长循环的长度。
(3)Map<GraphNode, Integer> pathVisited
:存储路径上访问过的节点和对应的步数。比如A -> B -> C -> A,如果从A开始,Map会变成{A -> 1, B -> 2, C -> 3},再次进入A时,step变成4, 因此循环长度为 4 - 1 = 3
(4)Set<GraphNode> visited
: 包含已经完全探索的graphNodes。
dfs(GraphNode cur, int curStep, Set<GraphNode> visited, Map<GraphNode, Integer> cache, Map<GraphNode, Integer> pathVisited):
if visited.containsKey(cur), then
return // if the node have been explored, no need to explore again
// if see a cycle, update the results(cache)
if pathVisited.containsKey(cur), then
newCycleLen = curStep - pathVisited.get(cur)
cache.put(cur, max {newCycleLen, cache.get(cur)})
return
// no cycle yet, continue the search
pathVisited.put(cur, curStep)
for each neighbor of cur:
dfs(neighbor, curStep + 1, cache, pathVisited)
endfor
visited.add(cur); // current node have been explored, in the future no need to visit it again.
path.remove(cur)
我感觉上面的算法可以在O(|V| + |E|)时间内解决问题,因为完全探索完一个节点后,我们就不会再在该节点上启动dfs了。
谁能给我一些提示,说明为什么上面的算法是错误的?
考虑下图:
D -- A
/ | |
E | |
\ | |
C -- B
现在,假设您在节点 A 开始 DFS 并按顺序访问节点 B,然后是 C,然后是 D,然后是 E。除非我误解了您的代码在做什么,否则我不相信这个访问顺序将允许你发现最长的循环 A、B、C、E、D、A,因为一旦你访问了 D,你就给它分配了错误的深度并切断了返回 A 的路径。
我认为问题是 "elementary cycle"。如果循环所有点都访问一次,则 DFS 很好。但这是个问题:
A---B
|\ /|
| E |
|/ \|
C---D
假定方向:
A -> B
B -> D,E
C -> D
D -> E,A
E -> C
DFS会发现最长的循环是5,A->B->E->C->D->A 但最长的循环应该是A->B->E->C->D->E ->A。也许你应该尝试使用新视图
问题是:给定一个有向图,找出图中最长的简单环。我搜索了这个问题并找到了这个 link Finding the longest cycle in a directed graph using DFS。答案说明这是一个NP难题。
但是我对下面的算法感到困惑,它似乎在 O(|V| + |E|) 时间内 运行 因为我们只访问了每条边一次。
维护以下变量:
(1) global max
:图中最长循环的长度。
(2) Map<GraphNode, Integer> cache
:存储从关键节点开始的最长循环的长度。
(3)Map<GraphNode, Integer> pathVisited
:存储路径上访问过的节点和对应的步数。比如A -> B -> C -> A,如果从A开始,Map会变成{A -> 1, B -> 2, C -> 3},再次进入A时,step变成4, 因此循环长度为 4 - 1 = 3
(4)Set<GraphNode> visited
: 包含已经完全探索的graphNodes。
dfs(GraphNode cur, int curStep, Set<GraphNode> visited, Map<GraphNode, Integer> cache, Map<GraphNode, Integer> pathVisited):
if visited.containsKey(cur), then
return // if the node have been explored, no need to explore again
// if see a cycle, update the results(cache)
if pathVisited.containsKey(cur), then
newCycleLen = curStep - pathVisited.get(cur)
cache.put(cur, max {newCycleLen, cache.get(cur)})
return
// no cycle yet, continue the search
pathVisited.put(cur, curStep)
for each neighbor of cur:
dfs(neighbor, curStep + 1, cache, pathVisited)
endfor
visited.add(cur); // current node have been explored, in the future no need to visit it again.
path.remove(cur)
我感觉上面的算法可以在O(|V| + |E|)时间内解决问题,因为完全探索完一个节点后,我们就不会再在该节点上启动dfs了。
谁能给我一些提示,说明为什么上面的算法是错误的?
考虑下图:
D -- A
/ | |
E | |
\ | |
C -- B
现在,假设您在节点 A 开始 DFS 并按顺序访问节点 B,然后是 C,然后是 D,然后是 E。除非我误解了您的代码在做什么,否则我不相信这个访问顺序将允许你发现最长的循环 A、B、C、E、D、A,因为一旦你访问了 D,你就给它分配了错误的深度并切断了返回 A 的路径。
我认为问题是 "elementary cycle"。如果循环所有点都访问一次,则 DFS 很好。但这是个问题:
A---B
|\ /|
| E |
|/ \|
C---D
假定方向:
A -> B
B -> D,E
C -> D
D -> E,A
E -> C
DFS会发现最长的循环是5,A->B->E->C->D->A 但最长的循环应该是A->B->E->C->D->E ->A。也许你应该尝试使用新视图