leetcode
leetcode 2451 ~ 2500
有向图访问计数

有向图访问计数

难度:

标签:

题目描述

There is a directed graph consisting of n nodes numbered from 0 to n - 1 and n directed edges.

You are given a 0-indexed array edges where edges[i] indicates that there is an edge from node i to node edges[i].

Consider the following process on the graph:

  • You start from a node x and keep visiting other nodes through edges until you reach a node that you have already visited before on this same process.

Return an array answer where answer[i] is the number of different nodes that you will visit if you perform the process starting from node i.

 

Example 1:

Input: edges = [1,2,0,0]
Output: [3,3,3,4]
Explanation: We perform the process starting from each node in the following way:
- Starting from node 0, we visit the nodes 0 -> 1 -> 2 -> 0. The number of different nodes we visit is 3.
- Starting from node 1, we visit the nodes 1 -> 2 -> 0 -> 1. The number of different nodes we visit is 3.
- Starting from node 2, we visit the nodes 2 -> 0 -> 1 -> 2. The number of different nodes we visit is 3.
- Starting from node 3, we visit the nodes 3 -> 0 -> 1 -> 2 -> 0. The number of different nodes we visit is 4.

Example 2:

Input: edges = [1,2,3,4,0]
Output: [5,5,5,5,5]
Explanation: Starting from any node we can visit every node in the graph in the process.

 

Constraints:

  • n == edges.length
  • 2 <= n <= 105
  • 0 <= edges[i] <= n - 1
  • edges[i] != i

代码结果

运行时间: 249 ms, 内存: 92.6 MB


/*
 * Problem Description:
 * Given a directed graph with n nodes labeled from 0 to n - 1, and an array edges,
 * where edges[i] represents a directed edge from node i to node edges[i].
 * For each node, determine the number of unique nodes that can be visited starting from that node
 * until a node is revisited in the process.
 *
 * Approach:
 * - We use a stream to create a range of integers representing each node.
 * - For each node, we calculate the number of unique nodes visited using a HashSet.
 */

import java.util.*;
import java.util.stream.IntStream;

public class GraphTraversalStream {
    public int[] countUniqueNodes(int[] edges) {
        return IntStream.range(0, edges.length)
                .map(i -> {
                    Set<Integer> visited = new HashSet<>();
                    int current = i;
                    while (visited.add(current)) {
                        current = edges[current];
                    }
                    return visited.size();
                })
                .toArray();
    }

    public static void main(String[] args) {
        GraphTraversalStream gts = new GraphTraversalStream();
        int[] edges1 = {1, 2, 0, 0};
        int[] edges2 = {1, 2, 3, 4, 0};
        System.out.println(Arrays.toString(gts.countUniqueNodes(edges1))); // Output: [3, 3, 3, 4]
        System.out.println(Arrays.toString(gts.countUniqueNodes(edges2))); // Output: [5, 5, 5, 5, 5]
    }
}

解释

方法:

该题解使用了深度优先搜索(DFS)来探索图,并借助额外的数组来存储每个节点开始的遍历信息。从每个节点开始,使用DFS遍历直到遇到已访问的节点,从而检测出环。\n\n1. 使用`ans`数组存储每个节点开始的遍历可以访问的不同节点数。\n2. 使用`depth`数组记录每个节点在当前DFS路径的深度。\n3. `fixRing`函数用于更新在环中的所有节点的访问节点数。\n4. `dfs`函数用于深度优先搜索,它检查下一个节点是否已被解决(即`ans[nextNode] > 0`),或者是否已在当前路径上访问过以形成环(即`depth[nextNode] > 0`)。根据情况,更新当前节点的访问节点数或调用`fixRing`函数。\n5. 主循环确保从每个尚未解决的节点开始DFS。\n\n通过这种方式,每个节点的访问计数被逐步建立,直到图中所有节点的访问计数都被确定。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在`dfs`函数中,当遇到已被访问的节点时,如何确保不会错误地重复计数或错过某些节点的计数?
在`dfs`函数中,当遇到一个节点`nextNode`已经被访问过(即`ans[nextNode] > 0`),说明从`nextNode`开始的所有可能的访问节点数已经被确定。因此,当前节点`curNode`的访问节点数应等于`nextNode`的访问节点数加上`nextNode`本身,即`ans[curNode] = ans[nextNode] + 1`。这种方式确保了每个节点的访问计数只被计算一次,避免了重复计数。同时,由于每个节点只在其访问计数未被解决时进行深度优先搜索,这避免了错过某些节点的计数。
🦆
题解中提到使用`depth`数组记录每个节点在当前DFS路径的深度,这样的机制是如何帮助检测环的存在以及确定环的长度的?
`depth`数组记录每个节点在当前DFS路径中的深度。当在DFS过程中遇到一个已在当前DFS路径上的节点`nextNode`(即`depth[nextNode] > 0`时),意味着从当前节点到`nextNode`形成了一个环。环的长度可以通过当前节点的深度`curDepth`减去`nextNode`的深度`depth[nextNode]`再加1得到,即`curDepth - depth[nextNode] + 1`。这个长度直接用于修正环中每个节点的访问节点数。
🦆
函数`fixRing`被设计来更新环中每个节点的访问节点数,那么在更新过程中如何保证不会对环外的节点造成影响?
`fixRing`函数在更新环中每个节点的访问节点数时,只会处理那些`ans`值为0的节点,即那些尚未解决访问计数的节点。函数从当前节点开始,沿着`edges`数组指示的方向遍历,直到遇到一个其`ans`值已非0的节点,此时停止更新。这样保证了只更新环中的节点。由于环外的节点在这个过程中`ans`值不是0,所以不会被错误地更新。
🦆
在题解的描述中,如果存在多个环或者环交叉的复杂情况,该算法是否仍然有效?具体是如何处理这种情况的?
该算法即使在存在多个环或环交叉的复杂情况下也是有效的。算法通过从每个节点开始的独立DFS确保每个节点的访问计数能够被正确计算。对于每个起始节点,DFS搜索会记录路径并通过`depth`数组检测和处理环。即使环相交或多个环存在,每次遇到环的处理(通过`fixRing`函数)确保了在该环中每个节点的访问计数都能正确更新,而不受其他环的影响。因此,无论环的结构如何复杂,每个节点的访问计数都会在其对应的DFS中被正确解决。

相关问题