leetcode
leetcode 2451 ~ 2500
可以到达每一个节点的最少边反转次数

可以到达每一个节点的最少边反转次数

难度:

标签:

题目描述

There is a simple directed graph with n nodes labeled from 0 to n - 1. The graph would form a tree if its edges were bi-directional.

You are given an integer n and a 2D integer array edges, where edges[i] = [ui, vi] represents a directed edge going from node ui to node vi.

An edge reversal changes the direction of an edge, i.e., a directed edge going from node ui to node vi becomes a directed edge going from node vi to node ui.

For every node i in the range [0, n - 1], your task is to independently calculate the minimum number of edge reversals required so it is possible to reach any other node starting from node i through a sequence of directed edges.

Return an integer array answer, where answer[i] is the minimum number of edge reversals required so it is possible to reach any other node starting from node i through a sequence of directed edges.

 

Example 1:

Input: n = 4, edges = [[2,0],[2,1],[1,3]]
Output: [1,1,0,2]
Explanation: The image above shows the graph formed by the edges.
For node 0: after reversing the edge [2,0], it is possible to reach any other node starting from node 0.
So, answer[0] = 1.
For node 1: after reversing the edge [2,1], it is possible to reach any other node starting from node 1.
So, answer[1] = 1.
For node 2: it is already possible to reach any other node starting from node 2.
So, answer[2] = 0.
For node 3: after reversing the edges [1,3] and [2,1], it is possible to reach any other node starting from node 3.
So, answer[3] = 2.

Example 2:

Input: n = 3, edges = [[1,2],[2,0]]
Output: [2,0,1]
Explanation: The image above shows the graph formed by the edges.
For node 0: after reversing the edges [2,0] and [1,2], it is possible to reach any other node starting from node 0.
So, answer[0] = 2.
For node 1: it is already possible to reach any other node starting from node 1.
So, answer[1] = 0.
For node 2: after reversing the edge [1, 2], it is possible to reach any other node starting from node 2.
So, answer[2] = 1.

 

Constraints:

  • 2 <= n <= 105
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ui == edges[i][0] < n
  • 0 <= vi == edges[i][1] < n
  • ui != vi
  • The input is generated such that if the edges were bi-directional, the graph would be a tree.

代码结果

运行时间: 357 ms, 内存: 101.0 MB


/*
 * 使用Java Stream API来构建图,并计算每个节点的最小反转次数。
 * 利用广度优先搜索(BFS)来计算每个节点作为起点到达其他所有节点的最少反转次数。
 * 用Dijkstra算法求解每个节点作为起点的最短路径。
 */
import java.util.*;
import java.util.stream.*;

public class SolutionStream {
    public int[] minReversals(int n, int[][] edges) {
        List<int[]>[] graph = IntStream.range(0, n).mapToObj(i -> new ArrayList<int[]>()).toArray(ArrayList[]::new);
        Arrays.stream(edges).forEach(edge -> {
            graph[edge[0]].add(new int[]{edge[1], 0});
            graph[edge[1]].add(new int[]{edge[0], 1});
        });

        return IntStream.range(0, n).map(i -> dijkstra(graph, n, i)).toArray();
    }

    private int dijkstra(List<int[]>[] graph, int n, int start) {
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[start] = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
        pq.add(new int[]{start, 0});

        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int node = current[0];
            int distance = current[1];

            if (distance > dist[node]) continue;

            for (int[] edge : graph[node]) {
                int neighbor = edge[0];
                int weight = edge[1];
                int newDist = dist[node] + weight;
                if (newDist < dist[neighbor]) {
                    dist[neighbor] = newDist;
                    pq.add(new int[]{neighbor, newDist});
                }
            }
        }

        return Arrays.stream(dist).max().orElse(Integer.MAX_VALUE);
    }
}

解释

方法:

这个题解采用了深度优先搜索(DFS)来解决问题。首先,构建一个图的邻接表,每个边(u, v)不仅记录目标节点v,还记录这条边的转向,如果是正向(u到v),则标记为1,反之为0。接着,从节点0开始,对图进行一次DFS遍历,计算从节点0出发到达所有节点的最少边反转次数,并记录下来。在这个过程中,计算从任意节点i到每个节点的相对深度以及到达该节点经过的正向边的数量。这些信息用于之后计算从其他节点出发到达所有节点的最少反转次数。最后,使用这些信息,通过一次循环计算出从每个节点出发的最少反转次数。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在构建邻接表时,你是如何决定每条边的转向标记(正向为1,反向为0)的?
在构建邻接表时,每条边的转向标记是根据边的输入方向决定的。如果边是从节点u到节点v,那么这条边在邻接表中对于u的记录是正向(标记为1),对于v的记录则是反向(标记为0)。这种方式可以确保无论边的原始方向如何,我们都能记录从任一节点出发到另一节点的正反两个方向,从而在后续的DFS搜索中能够计算出边的反转次数。
🦆
DFS遍历时,函数`dfs`的参数`posAccu`具体代表什么意义?它是如何帮助计算从某一节点出发的最少边反转次数的?
`posAccu`参数代表从起始节点到当前节点路径上的正向边的累积数量。在DFS过程中,每遍历到一个节点,都会根据当前边是正向还是反向来更新这个累积值。这个值对计算最少边反转次数至关重要,因为它帮助我们了解到达当前节点时不需要反转的边的数量,从而能够计算出需要反转的边的总数,这直接影响到从起始点到任一节点的最少反转次数的计算。
🦆
题解中提到计算从节点0开始的最少边反转次数,为什么选取节点0作为起始点?在有向图中,选择不同的起始节点是否会影响最终的边反转次数?
通常在算法问题中,如果没有特别指定,节点0常被默认为起始点,这是一种常见的假设。实际上,如果图是连通的,选择不同的起始节点可能会影响到达各个节点的最少边反转次数的绝对值,但是整体的最优解的模式(即哪些边需要反转)通常是不变的。在非连通图中,起始节点的选择会更加关键,因为某些节点可能从当前起始节点根本不可达。
🦆
在DFS过程中,如何处理图中可能存在的环?特别是在有向图中,环的存在是否会影响边反转的计算?
在DFS过程中处理环的一种常见方式是通过标记已访问的节点来避免重复访问,从而防止无限循环。对于有向图中的环,其存在确实会影响边反转的计算,因为环可能允许通过不同的路径到达同一个节点,这些路径可能需要不同数量的边反转。为了准确计算最少边反转次数,算法需要能够识别并比较通过环的不同路径所需的边反转次数。在实际实现中,可以用额外的数据结构(如`visited`数组)来跟踪访问过的节点和路径,以确保正确处理环路。

相关问题