📜  使用 BFS 检测无向图中的循环(1)

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

使用 BFS 检测无向图中的循环

在无向图中,循环指的是经过至少三个节点的路径,并且起点和终点相同。在这里,我们将介绍如何使用广度优先搜索(BFS)算法来检测无向图中的循环。

算法思路

BFS是基于队列(queue)的搜索算法。该算法从源节点开始,依次访问源节点的每一个邻居,并将其邻居加入队列,直到队列为空。在访问每一个邻居节点时,我们检查是否已经被访问,如果是,说明这个图中存在循环。

在实现中,我们可以用一个visited数组来记录每一个节点是否被访问。同时,我们在队列中保存每一个节点及其上一个访问的节点(parent)。如果某一节点的邻居已经在visited数组中,且该节点不是其parent,那么说明图中存在循环。

代码实现

首先,我们定义一个Graph类,存储无向图。在该类中,我们定义两个属性:num_vertices表示节点数,adj_list表示邻接表。

class Graph:
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        self.adj_list = {i: [] for i in range(num_vertices)}
    
    def add_edge(self, u, v):
        self.adj_list[u].append(v)
        self.adj_list[v].append(u)

接着,我们定义一个has_cycle函数,用于检测无向图中是否存在循环。该函数采用BFS算法来遍历图。

from collections import deque

def has_cycle(graph):
    visited = [False] * graph.num_vertices
    for i in range(graph.num_vertices):
        if not visited[i]:
            if bfs(graph, visited, i):
                return True
    return False

def bfs(graph, visited, start):
    q = deque()
    q.append((start, None))
    visited[start] = True
    while q:
        node, parent = q.popleft()
        for neighbor in graph.adj_list[node]:
            if not visited[neighbor]:
                visited[neighbor] = True
                q.append((neighbor, node))
            elif neighbor != parent:
                return True
    return False

has_cycle函数中,我们先将visited数组初始化为False。对于每一个节点,我们检查其是否被访问。如果没有被访问,我们利用bfs函数遍历该节点的邻居。在bfs函数中,我们利用队列来遍历节点的邻居,并且通过parent来避免重复搜索已经访问过的节点。如果某一个邻居已经被visited且不是parent,那么说明存在循环。

测试

我们用以下代码来测试has_cycle函数的正确性:

g = Graph(4)
g.add_edge(0, 1)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)

assert has_cycle(g) == True

g = Graph(5)
g.add_edge(0, 1)
g.add_edge(1, 2)
g.add_edge(2, 3)
g.add_edge(3, 4)

assert has_cycle(g) == False

在第一个测试用例中,我们构建了一个存在循环的图,第二个测试用例中我们构建了一个不存在循环的图。运行测试用例后,我们可以看到所有测试用例均通过。