class Solution {
public:
vector<int> countVisitedNodes(vector<int>& edges) {
}
};
100075. 有向图访问计数
现有一个有向图,其中包含 n
个节点,节点编号从 0
到 n - 1
。此外,该图还包含了 n
条有向边。
给你一个下标从 0 开始的数组 edges
,其中 edges[i]
表示存在一条从节点 i
到节点 edges[i]
的边。
想象在图上发生以下过程:
x
开始,通过边访问其他节点,直到你在 此过程 中再次访问到之前已经访问过的节点。返回数组 answer
作为答案,其中 answer[i]
表示如果从节点 i
开始执行该过程,你可以访问到的不同节点数。
示例 1:
输入:edges = [1,2,0,0] 输出:[3,3,3,4] 解释:从每个节点开始执行该过程,记录如下: - 从节点 0 开始,访问节点 0 -> 1 -> 2 -> 0 。访问的不同节点数是 3 。 - 从节点 1 开始,访问节点 1 -> 2 -> 0 -> 1 。访问的不同节点数是 3 。 - 从节点 2 开始,访问节点 2 -> 0 -> 1 -> 2 。访问的不同节点数是 3 。 - 从节点 3 开始,访问节点 3 -> 0 -> 1 -> 2 -> 0 。访问的不同节点数是 4 。
示例 2:
输入:edges = [1,2,3,4,0] 输出:[5,5,5,5,5] 解释:无论从哪个节点开始,在这个过程中,都可以访问到图中的每一个节点。
提示:
n == edges.length
2 <= n <= 105
0 <= edges[i] <= n - 1
edges[i] != i
原站题解
python3 解法, 执行用时: 832 ms, 内存消耗: 47 MB, 提交时间: 2023-10-02 00:23:04
class Solution: def countVisitedNodes(self, g: List[int]) -> List[int]: n = len(g) rg = [[] for _ in range(n)] # 反图 deg = [0] * n for x, y in enumerate(g): rg[y].append(x) deg[y] += 1 # 拓扑排序,剪掉 g 上的所有树枝 # 拓扑排序后,deg 值为 1 的点必定在基环上,为 0 的点必定在树枝上 q = deque(i for i, d in enumerate(deg) if d == 0) while q: x = q.popleft() y = g[x] deg[y] -= 1 if deg[y] == 0: q.append(y) ans = [0] * n # 在反图上遍历树枝 def rdfs(x: int, depth: int) -> None: ans[x] = depth for y in rg[x]: if deg[y] == 0: # 树枝上的点在拓扑排序后,入度均为 0 rdfs(y, depth + 1) for i, d in enumerate(deg): if d <= 0: continue ring = [] x = i while True: deg[x] = -1 # 将基环上的点的入度标记为 -1,避免重复访问 ring.append(x) # 收集在基环上的点 x = g[x] if x == i: break for x in ring: rdfs(x, len(ring)) # 为方便计算,以 len(ring) 作为初始深度 return ans
java 解法, 执行用时: 117 ms, 内存消耗: 72.8 MB, 提交时间: 2023-10-02 00:22:50
class Solution { public int[] countVisitedNodes(List<Integer> edges) { int[] g = edges.stream().mapToInt(i -> i).toArray(); int n = g.length; List<Integer>[] rg = new ArrayList[n]; // 反图 Arrays.setAll(rg, e -> new ArrayList<>()); int[] deg = new int[n]; for (int x = 0; x < n; x++) { int y = g[x]; rg[y].add(x); deg[y]++; } // 拓扑排序,剪掉 g 上的所有树枝 // 拓扑排序后,deg 值为 1 的点必定在基环上,为 0 的点必定在树枝上 var q = new ArrayDeque<Integer>(); for (int i = 0; i < n; i++) { if (deg[i] == 0) { q.add(i); } } while (!q.isEmpty()) { int x = q.poll(); int y = g[x]; if (--deg[y] == 0) { q.add(y); } } int[] ans = new int[n]; for (int i = 0; i < n; i++) { if (deg[i] <= 0) { continue; } var ring = new ArrayList<Integer>(); for (int x = i; ; x = g[x]) { deg[x] = -1; // 将基环上的点的入度标记为 -1,避免重复访问 ring.add(x); // 收集在基环上的点 if (g[x] == i) { break; } } for (int r : ring) { rdfs(r, ring.size(), rg, deg, ans); // 为方便计算,以 ring.size() 作为初始深度 } } return ans; } // 在反图上遍历树枝 private void rdfs(int x, int depth, List<Integer>[] rg, int[] deg, int[] ans) { ans[x] = depth; for (int y : rg[x]) { if (deg[y] == 0) { // 树枝上的点在拓扑排序后,入度均为 0 rdfs(y, depth + 1, rg, deg, ans); } } } }
cpp 解法, 执行用时: 464 ms, 内存消耗: 220.6 MB, 提交时间: 2023-10-02 00:22:35
class Solution { public: vector<int> countVisitedNodes(vector<int> &g) { int n = g.size(); vector<vector<int>> rg(n); // 反图 vector<int> deg(n); for (int x = 0; x < n; x++) { int y = g[x]; rg[y].push_back(x); deg[y]++; } // 拓扑排序,剪掉 g 上的所有树枝 // 拓扑排序后,deg 值为 1 的点必定在基环上,为 0 的点必定在树枝上 queue<int> q; for (int i = 0; i < n; i++) { if (deg[i] == 0) { q.push(i); } } while (!q.empty()) { int x = q.front(); q.pop(); int y = g[x]; if (--deg[y] == 0) { q.push(y); } } vector<int> ans(n, 0); // 在反图上遍历树枝 function<void(int, int)> rdfs = [&](int x, int depth) { ans[x] = depth; for (int y: rg[x]) { if (deg[y] == 0) { // 树枝上的点在拓扑排序后,入度均为 0 rdfs(y, depth + 1); } } }; for (int i = 0; i < n; i++) { if (deg[i] <= 0) { continue; } vector<int> ring; for (int x = i;; x = g[x]) { deg[x] = -1; // 将基环上的点的入度标记为 -1,避免重复访问 ring.push_back(x); // 收集在基环上的点 if (g[x] == i) { break; } } for (int x: ring) { rdfs(x, ring.size()); // 为方便计算,以 ring.size() 作为初始深度 } } return ans; } };
golang 解法, 执行用时: 284 ms, 内存消耗: 19.2 MB, 提交时间: 2023-10-02 00:22:20
func countVisitedNodes(g []int) []int { n := len(g) rg := make([][]int, n) // 反图 deg := make([]int, n) for x, y := range g { rg[y] = append(rg[y], x) deg[y]++ } // 拓扑排序,剪掉 g 上的所有树枝 // 拓扑排序后,deg 值为 1 的点必定在基环上,为 0 的点必定在树枝上 q := []int{} for i, d := range deg { if d == 0 { q = append(q, i) } } for len(q) > 0 { x := q[0] q = q[1:] y := g[x] deg[y]-- if deg[y] == 0 { q = append(q, y) } } ans := make([]int, n) // 在反图上遍历树枝 var rdfs func(int, int) rdfs = func(x, depth int) { ans[x] = depth for _, y := range rg[x] { if deg[y] == 0 { // 树枝上的点在拓扑排序后,入度均为 0 rdfs(y, depth+1) } } } for i, d := range deg { if d <= 0 { continue } ring := []int{} for x := i; ; x = g[x] { deg[x] = -1 // 将基环上的点的入度标记为 -1,避免重复访问 ring = append(ring, x) // 收集在基环上的点 if g[x] == i { break } } for _, x := range ring { rdfs(x, len(ring)) // 为方便计算,以 len(ring) 作为初始深度 } } return ans }