📜  在有向图中检测循环(1)

📅  最后修改于: 2023-12-03 15:37:43.632000             🧑  作者: Mango

在有向图中检测循环

在有向图中,如果存在一组节点,满足从某一个节点开始,经过某些边可以回到该节点,那么称这个图为有向图中存在循环。

思路

针对有向图中的循环,我们可以考虑使用深度遍历和拓扑排序两种方法来检测。

深度遍历
  • 我们可以使用一个visited数组记录节点是否被访问过,每经过一个节点,就将该节点的visited值置为true。
  • 对于当前节点的所有邻居节点(即它直接指向的节点),如果该邻居节点之前已经被访问过,说明当前路径出现了循环,直接返回true。
  • 如果邻居节点没有被访问过,就递归访问该邻居节点。如果该邻居节点出现了循环,则递归返回true。
  • 如果所有邻居节点都已被访问过,且没有循环出现,则返回false。
def has_cycle_dfs(graph, visited, current):
    # 当前节点被访问过,说明出现循环
    if visited[current]:
        return True
    # 当前节点设为访问过
    visited[current] = True
    # 遍历当前节点的邻居节点
    for neighbor in graph[current]:
        # 如果邻居节点已被访问过,说明出现循环
        if visited[neighbor]:
            return True
        # 如果邻居节点没有被访问过,递归访问
        if has_cycle_dfs(graph, visited, neighbor):
            return True
    # 遍历所有邻居节点后没有发现循环,返回false
    visited[current] = False
    return False
拓扑排序
  • 我们可以使用一个in_degree数组来记录每个节点的入度(即有多少个节点指向该节点)。
  • 将所有入度为0的节点加入队列,并将它们从图中删除(即把指向它们的边去掉)。
  • 对于每个被删除的节点,遍历它的所有邻居,并将它邻居节点的入度减1。
  • 如果有任意节点的入度变为0,则将该节点加入队列。
  • 如果队列为空后不是所有节点都被删除(即图中仍存在节点),则说明存在循环。
from collections import deque

def has_cycle_topo(graph, in_degree):
    # 将所有入度为0的节点加入队列
    queue = deque([i for i, node in enumerate(in_degree) if node == 0])
    # 记录已删除的节点数
    count = 0
    while queue:
        node = queue.popleft()
        count += 1
        for neighbor in graph[node]:
            # 将邻居节点的入度减1
            in_degree[neighbor] -= 1
            # 如果邻居节点的入度减为0,则加入队列
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    # 如果所有节点都被删除,则不存在循环
    return count != len(graph)
总结

以上就是在有向图中检测循环的两种方法:深度遍历和拓扑排序。可以根据具体情况选择使用哪种方法来解决问题。