📌  相关文章
📜  不属于有向图的任何循环的打印节点(1)

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

不属于有向图的任何循环的打印节点

有向图中的循环是指从一个节点出发,经过若干个节点后,最终回到原始节点。循环也被称为周期或环路。在这样的循环中,您无法确定从哪个节点开始或结束。

以下是一些有用的定义:

  • 有向图:图形,其中边是有方向的,意味着从一个节点到另一个节点的路径只能遵循一个特定的方向。
  • 节点:图中的一个点,通常代表一个实体。
  • 边缘:两个节点之间的有向连接。
  • 入度:指向给定节点的边数。
  • 出度:从给定节点开始的边数。

在有向图中,不属于任何循环的节点可以被视为独立节点。这些节点可以没有任何入边和出边,也可以只有入边或出边。例如,在一个拓扑排序中,这些节点总是排在图的最前面。

做法

一个节点不属于任何循环,当且仅当这个节点被访问后,无法回到这个节点。或者说,这个节点无法被后续访问到。这些被访问过的节点就是所谓的强连通图组成的集合。其中一个很简单的做法是使用Tarjan's算法。

Tarjan's算法是一种基于深度优先搜索(DFS)的算法,用于查找图中的强连通组件。

其基本思想是:

  1. 对图进行深度优先搜索,在搜索过程中,记录下访问每个节点的次序(时间戳)和最小回溯时间戳。
  2. 如果搜索一个节点的过程中,可以发现一个"后代"的最小回溯时间戳等于这个节点的时间戳,则将这些节点都加入同一个强连通组件中。
  3. 如果搜索完了一个强连通组件,则从栈中弹出这个强连通组件中的所有点。
代码示例
class Graph:
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        self.adj_list = [[] for _ in range(num_vertices)]
  
    def add_edge(self, u, v):
        self.adj_list[u].append(v)
  
    def tarjan(self):
        time_stamp = 0
        stack, low, discovered, on_stack = [], [], [], []
        for vertex in range(self.num_vertices):
            low.append(-1)
            discovered.append(-1)
            on_stack.append(False)
        for vertex in range(self.num_vertices):
            if discovered[vertex] == -1:
                time_stamp = self._tarjan(vertex, time_stamp, low, discovered, stack, on_stack)
        return low

    def _tarjan(self, vertex, time_stamp, low, discovered, stack, on_stack):
        discovered[vertex] = time_stamp
        low[vertex] = time_stamp
        time_stamp += 1
        stack.append(vertex)
        on_stack[vertex] = True
        for adj_vertex in self.adj_list[vertex]:
            if discovered[adj_vertex] == -1:
                time_stamp = self._tarjan(adj_vertex, time_stamp, low, discovered, stack, on_stack)
                low[vertex] = min(low[vertex], low[adj_vertex])
            elif on_stack[adj_vertex]:
                low[vertex] = min(low[vertex], discovered[adj_vertex])
        if low[vertex] == discovered[vertex]:
            while True:
                adj_vertex = stack.pop()
                on_stack[adj_vertex] = False
                if adj_vertex == vertex:
                    break
        return time_stamp

# 示例代码
g = Graph(7)
g.add_edge(0, 1)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(1, 3)
g.add_edge(1, 4)
g.add_edge(1, 6)
g.add_edge(3, 5)
g.add_edge(4, 5)
low = g.tarjan()
print("Low values:", low)
print("Nodes not in any cycle:", [idx for idx, val in enumerate(low) if val == idx])

上面的代码展示了如何通过使用Tarjan's算法来查找图中的所有强连通组件,并标识那些不属于任何循环的节点。