Java强连通分量(含面试大厂题和源码)

强连通分量(Strongly Connected Components, SCCs)是图论中的一个概念,主要用于有向图。在有向图中,如果从图中的某个顶点 A 能够到达另一个顶点 B,并且从顶点 B 也能到达顶点 A,则称这两个顶点是强连通的。一个强连通分量是这样一个子图,其内部任意两个顶点都是强连通的。

强连通分量的算法:

  1. Kosaraju 算法:这是一个经典的算法,它首先对原图进行深度优先搜索(DFS),记录顶点的完成顺序,并建立一个逆序的顶点栈。然后,对原图的逆图(即边的方向全部反转)再次进行 DFS,每次 DFS 会找到原图中的一个强连通分量。

  2. Tarjan 算法:Tarjan 算法也是一种用于寻找强连通分量的算法,它在同一个图中完成寻找过程,不需要构建逆图。Tarjan 算法在 DFS 的过程中为每个顶点分配三个值:发现时间戳(disc)、最低可达时间戳(low),以及一个标记用来判断顶点是否在栈上。

强连通分量的 Java 实现(Kosaraju 算法):

import java.util.*;

public class StronglyConnectedComponents {
    private int time;
    private List<List<Integer>> adjList;
    private Stack<Integer> postOrder;
    private List<List<Integer>> sccList;

    public List<List<Integer>> getSCCs(int n, List<List<Integer>> edges) {
        adjList = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            adjList.add(new ArrayList<>());
        }
        postOrder = new Stack<>();
        sccList = new ArrayList<>();
        time = 0;

        // Build the adjacency list and perform DFS on the original graph
        for (List<Integer> edge : edges) {
            adjList.get(edge.get(0)).add(edge.get(1));
        }
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                dfs(i);
            }
        }

        // Create the transpose of the graph
        List<List<Integer>> transpose = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            transpose.add(new ArrayList<>());
        }
        for (List<Integer> edge : edges) {
            transpose.get(edge.get(1)).add(edge.get(0));
        }

        // Perform DFS on the transpose graph
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                dfsOnTranspose(transpose, i);
            }
        }

        return sccList;
    }

    private void dfs(int v) {
        visited[v] = true;
        for (int neighbor : adjList.get(v)) {
            if (!visited[neighbor]) {
                dfs(neighbor);
            }
        }
        postOrder.push(v);
    }

    private void dfsOnTranspose(List<List<Integer>> transpose, int v) {
        visited[v] = true;
        sccList.add(v);
        for (int neighbor : transpose.get(v)) {
            if (!visited[neighbor]) {
                dfsOnTranspose(transpose, neighbor);
            }
        }
    }

    // Helper method to initialize visited array and call getSCCs
    public List<List<Integer>> scc(int n, List<List<Integer>> edges) {
        boolean[] visited = new boolean[n];
        getSCCs(n, edges);
        return sccList;
    }

    public static void main(String[] args) {
        StronglyConnectedComponents scc = new StronglyConnectedComponents();
        int n = 5;
        List<List<Integer>> edges = Arrays.asList(
            Arrays.asList(1, 0),
            Arrays.asList(0, 2),
            Arrays.asList(2, 1),
            Arrays.asList(0, 3),
            Arrays.asList(3, 4)
        );
        List<List<Integer>> result = scc.scc(n, edges);
        System.out.println("Strongly Connected Components: " + result);
    }
}

在面试中,强连通分量是一个重要的图算法问题,它考察应聘者对图算法的理解和算法实现能力。通过实现强连通分量的算法,可以展示你对深度优先搜索和图算法的掌握程度。希望这些知识点和示例代码能够帮助你更好地准备面试!强连通分量(SCC)是图算法中的一个高级话题,经常出现在大厂的面试中。以下是三道与强连通分量相关的面试题目,以及相应的Java源码实现。

题目 1:社交网络中的社交圈子

描述
在社交网络中,如果两个人是朋友,那么他们之间是双向关注的。给定社交网络中的关注关系,找出所有的社交圈子。

示例

输入:社交网络关系图的邻接表表示,例如:
{
  1: [2],
  2: [1, 3],
  3: [2],
  4: [5],
  5: [4]
}
输出:[[1, 2, 3], [4, 5]]

Java 源码(使用Kosaraju算法):

import java.util.*;

public class SocialCircles {
    List<List<Integer>> sccList;
    int time;
    Stack<Integer> postOrder;
    Map<Integer, List<Integer>> graph;

    public List<List<Integer>> findCircles(int n, Map<Integer, List<Integer>> graph) {
        this.graph = new HashMap<>(graph);
        sccList = new ArrayList<>();
        time = 0;
        postOrder = new Stack<>();
        boolean[] visited = new boolean[n];

        // DFS on original graph
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                dfs(i, visited);
            }
        }

        // Create transpose of the graph
        Map<Integer, List<Integer>> transpose = new HashMap<>();
        for (Map.Entry<Integer, List<Integer>> entry : graph.entrySet()) {
            for (int neighbor : entry.getValue()) {
                if (!transpose.containsKey(neighbor)) {
                    transpose.put(neighbor, new ArrayList<>());
                }
                transpose.get(neighbor).add(entry.getKey());
            }
        }

        // DFS on transpose graph
        visited = new boolean[n];
        while (!postOrder.isEmpty()) {
            int node = postOrder.pop();
            if (!visited[node]) {
                dfsOnTranspose(transpose, node, visited, new ArrayList<>());
                sccList.add(visitedSubgraph);
            }
        }
        return sccList;
    }

    private void dfs(int node, boolean[] visited) {
        visited[node] = true;
        for (int neighbor : graph.getOrDefault(node, Collections.emptyList())) {
            if (!visited[neighbor]) {
                dfs(neighbor, visited);
            }
        }
        postOrder.push(node);
    }

    private void dfsOnTranspose(Map<Integer, List<Integer>> transpose, int node, boolean[] visited, List<Integer> visitedSubgraph) {
        visited[node] = true;
        visitedSubgraph.add(node);
        for (int neighbor : transpose.getOrDefault(node, Collections.emptyList())) {
            if (!visited[neighbor]) {
                dfsOnTranspose(transpose, neighbor, visited, visitedSubgraph);
            }
        }
    }

    public static void main(String[] args) {
        SocialCircles solution = new SocialCircles();
        int n = 5;
        Map<Integer, List<Integer>> graph = new HashMap<>();
        graph.put(1, Collections.singletonList(2));
        graph.put(2, Arrays.asList(1, 3));
        graph.put(3, Collections.singletonList(2));
        graph.put(4, Collections.singletonList(5));
        graph.put(5, Collections.singletonList(4));

        List<List<Integer>> result = solution.findCircles(n, graph);
        System.out.println("Social circles: " + result);
    }
}

题目 2:课程的先决条件

描述
给定 n 门课程和一个先决条件列表,请你检查是否存在课程之间的循环依赖,如果存在则返回 true;否则,返回 false。

示例

输入:n = 4, prerequisites = [[1, 0], [2, 0], [3, 1], [3, 2]]
输出:false

Java 源码

import java.util.*;

public class CoursePrerequisites {
    public boolean hasCycle(int n, int[][] prerequisites) {
        Map<Integer, List<Integer>> graph = new HashMap<>();
        int[] inDegree = new int[n];
        
        // Build the graph and calculate in-degrees
        for (int[] edge : prerequisites) {
            graph.computeIfAbsent(edge[1], k -> new ArrayList<>()).add(edge[0]);
            inDegree[edge[0]]++;
        }
        
        // Use a queue to perform BFS on the graph
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        int visitedCount = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int course = queue.poll();
                visitedCount++;
                for (int prereq : graph.get(course)) {
                    if (--inDegree[prereq] == 0) {
                        queue.offer(prereq);
                    }
                }
            }
        }
        
        return visitedCount != n;
    }

    public static void main(String[] args) {
        CoursePrerequisites solution = new CoursePrerequisites();
        int n = 4;
        int[][] prerequisites = {{1, 0}, [2, 0], [3, 1], [3, 2]};

        boolean result = solution.hasCycle(n, prerequisites);
        System.out.println("Does the course have a cycle of prerequisites? " + result);
    }
}

题目 3:构建离线图

描述
给定一个包含 n 个顶点和 m 个边的有向图,边具有权重。设计一个算法,根据给定的边权重,构建一个离线图,使得在离线图中,任意两个强连通分量之间的边的权重之和最小。

示例

输入:n = 4, edges = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 1, 6]]
输出:最小权重和

Java 源码

import java.util.*;

public class OfflineGraph {
    public int buildOfflineGraph(int n, int[][] edges) {
        Map<Integer, List<int[]>> graph = new HashMap<>();
        int[] inDegree = new int[n];
        int totalWeight = 0;
        
        // Build the graph and calculate in-degrees
        for (int[] edge : edges) {
            graph.computeIfAbsent(edge[1], k -> new ArrayList<>()).add(new int[]{edge[0], edge[2]});
            inDegree[edge[0]]++;
            totalWeight += edge[2];
        }
        
        // Use a queue to perform BFS on the graph
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int vertex = queue.poll();
                for (int[] edge : graph.get(vertex)) {
                    if (--inDegree[edge[0]] == 0) {
                        queue.offer(edge[0]);
                    }
                }
            }
        }
        
        // Calculate the remaining weight considering SCCs
        // (This part requires a more complex algorithm and is not fully implemented here)
        // ...

        return totalWeight; // Placeholder for the minimum weight sum
    }

    public static void main(String[] args) {
        OfflineGraph solution = new OfflineGraph();
        int n = 4;
        int[][] edges = {{1, 2, 3}, [2, 3, 4}, [3, 4, 5}, [4, 1, 6]];

        int result = solution.buildOfflineGraph(n, edges);
        System.out.println("Minimum weight sum of the offline graph: " + result);
    }
}

这些题目和源码展示了强连通分量在图算法中的应用。在面试中,能够根据问题的特点选择合适的算法并实现其解决方案是非常重要的。希望这些示例能够帮助你更好地准备面试!

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值