拓扑排序+dfs+内向基环树

每个连通块必定有且仅有一个环,且由于每个点的出度均为 11,这样的有向图又叫做内向基环树 

题解链接

class Solution {
    public int maximumInvitations(int[] favorite) {
        int n = favorite.length;
        Map<Integer, Integer> graph = new HashMap<>();  // 正图:i -> favorite[i]
        Map<Integer, List<Integer>> reGraph = new HashMap<>();  // 反图       
        int[] inDegree = new int[n];    // 记录入度信息        

        for (int i = 0; i < n; i++) {
            graph.put(i, favorite[i]);

            reGraph.putIfAbsent(favorite[i], new ArrayList<>());
            reGraph.get(favorite[i]).add(i);

            inDegree[favorite[i]]++;
        }

        // 拓扑排序进行剪枝处理
        Queue<Integer> que = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) que.offer(i);
        }

        while (!que.isEmpty()) {
            int curr = que.poll();
            inDegree[graph.get(curr)]--;
            if (inDegree[graph.get(curr)] == 0) {
                que.offer(graph.get(curr));
            }
        }

        // 根据处理后的入度信息来查找基环
        List<List<Integer>> baseCircles = findBaseCircle(favorite, inDegree);
       
        boolean[] vis = new boolean[n];
        for (int i = 0; i < n; i++) {
            // 入度大于0,表示在环上; 基环长度为2的时候,不能走这些在环上的点
            vis[i] = inDegree[i] > 0; 
        }

        int sumLength = 0;
        int max = Integer.MIN_VALUE;
        for (List<Integer> circle: baseCircles) {
            if (circle.size() > 2) {
                max = Math.max(circle.size(), max);
            } else { // 基环长度为2的时候,沿着反图查找最长路径
                sumLength += findLeftOrRightLength(circle.get(0), reGraph, vis) + findLeftOrRightLength(circle.get(1), reGraph, vis);
            }
        }

        return Math.max(max, sumLength);
    }

    // 递归寻找最长的路径
    private int findLeftOrRightLength(int curr, Map<Integer, List<Integer>> reGraph, boolean[] vis) {
        List<Integer> nextList = reGraph.get(curr);
        if (nextList == null) return 1;
        int max = 0;
        for (int next: nextList) {
            if (vis[next]) continue;
            max = Math.max(max, findLeftOrRightLength(next, reGraph, vis));
        }
        return max + 1;
    }

    private List<List<Integer>> findBaseCircle(int[] favorite, int[] inDegree) {
        int n = favorite.length;
        List<List<Integer>> baseCircles = new ArrayList<List<Integer>>();
        boolean[] vis = new boolean[n];

        for (int i = 0; i < n; i++) {
            // 入度为零 表示已经不在环上,标记为已访问即可
            vis[i] = inDegree[i] == 0;
        }

        for (int i = 0; i < n; i++) {
            if (vis[i]) continue;

            int next = i;
            List<Integer> baseCircle = new ArrayList<>();
            while (!vis[next]) {
                baseCircle.add(next);
                vis[next] = true;
                next = favorite[next];
            }
            baseCircles.add(baseCircle);
        }

        return baseCircles;
    }
}

 

posted @ 2022-01-02 19:31  Peterxiazhen  阅读(105)  评论(0编辑  收藏  举报