【Leetcode】图算法总结

Leetcode中图的算法是比较常见的类型,比如无向图的单源最短路径,有向图的单源最短路径,多源最短路径等问题,下面就对图的算法进行总结。

文章目录

      • 单源最短路径:Dijkstra算法
        • 743. 网络延迟时间
      • 拓扑排序
        • 210. 课程表 II
        • 207. 课程表

单源最短路径:Dijkstra算法

743. 网络延迟时间

1. 题目描述

leetcode题目链接:743. 网络延迟时间
【Leetcode】图算法总结_第1张图片
2. 思路分析

这道题目是有向图的单源最短路径,可以使用Dijkstra算法,也可以使用Flod算法。

Dijkstra算法每次取的都是队列中的最小元素,这里可以使用优先队列来实现。

连接关系,可以采用邻接表和邻接矩阵两种。

  • 邻接表适用于稀疏图
  • 邻接矩阵适用于稠密图

同时可以使用visited数组来记录是否使用过了(使用过的意思是被当做中间节点 )

3. 参考代码

方法一:Dijkstra算法的优先队列实现

使用邻接表表示连接关系

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        PriorityQueue<int[]> queue = new PriorityQueue<>((a, b) -> a[1] - b[1]);
        // boolean[] visited = new boolean[n + 1];
        int[] dist = new int[n + 1];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[k] = 0;
        queue.add(new int[]{k, 0});
        while (!queue.isEmpty()) {
            int[] poll = queue.poll();
            int node = poll[0];
            int dis = poll[1];
            for (int i = 0; i < times.length; i++) {
                if (times[i][0] == node) {
                    int next = times[i][1];
                    if (dist[next] > dist[node] + times[i][2]) {
                        dist[next] = dist[node] + times[i][2];
                        queue.add(new int[]{next, dist[next]});
                    }
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            if (dist[i] == Integer.MAX_VALUE) {
                return -1;
            }
            res = Math.max(res, dist[i]);
        }
        return res;
    }
}

使用邻接矩阵表示连接关系

class Solution {
    int maxValue = 999;
    public int networkDelayTime(int[][] times, int n, int k) {
        int source = k;
        int[][] graph = new int[n + 1][n + 1];  // 邻接矩阵
        int N = graph.length;        
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                graph[i][j] = (i == j ? 0 : maxValue);
            }
        }
        for(int i = 0; i < times.length; i++){
            int v = times[i][0];
            int w = times[i][1];
            int weight = times[i][2];
            graph[v][w] = weight;
        }
        int delay = dijkstra(source,graph);
        return delay;
    }
    public int dijkstra(int source,int[][] graph){
        int N = graph.length;
        int[] distTo = new int[N];
        // Queue queue = new LinkedList<>();
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        Arrays.fill(distTo,maxValue);
        distTo[source] = 0;
        queue.add(source);
        while(!queue.isEmpty()){
            int node = queue.poll();
            for(int next = 1; next < N; next++){
     
                if(distTo[node] + graph[node][next] < distTo[next]){
                    distTo[next] = distTo[node] + graph[node][next];
                    queue.add(next);
                }
            }
        }
        int ans = 0;
        for(int i = 1; i < N; i++){
            if(distTo[i] == maxValue)
                return -1;
            ans = Math.max(ans,distTo[i]);
        }
        return ans;
    }
}

方法二:SPFA算法

class Solution {
    int maxValue = 999;
    public int networkDelayTime(int[][] times, int n, int k) {
        int source = k;
        int[][] graph = new int[n + 1][n + 1];  // 邻接矩阵
        int N = graph.length;        
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                graph[i][j] = (i == j ? 0 : maxValue);
            }
        }
        for(int i = 0; i < times.length; i++){
            int v = times[i][0];
            int w = times[i][1];
            int weight = times[i][2];
            graph[v][w] = weight;
        }
        int delay = dijkstra(source,graph);
        return delay;
    }
    public int dijkstra(int source,int[][] graph){
        int N = graph.length;
        int[] distTo = new int[N];
        Queue<Integer> queue = new LinkedList<>();
        Arrays.fill(distTo,maxValue);
        distTo[source] = 0;
        queue.add(source);
        while(!queue.isEmpty()){
            int node = queue.poll();
            for(int next = 1; next < N; next++){
     
                if(distTo[node] + graph[node][next] < distTo[next]){
                    distTo[next] = distTo[node] + graph[node][next];
                    queue.add(next);
                }
            }
        }
        int ans = 0;
        for(int i = 1; i < N; i++){
            if(distTo[i] == maxValue)
                return -1;
            ans = Math.max(ans,distTo[i]);
        }
        return ans;
    }
}

区别就是是否每次取得最小路径。

另外使用邻接矩阵存储连接关系,效率更高。

拓扑排序

210. 课程表 II

leetcode链接:210. 课程表 II
【Leetcode】图算法总结_第2张图片
这道题看完就发现是拓扑排序的问题,则使用BFS解法。

  1. 记录各个节点的入度
  2. 记录每个节点所能达到的节点,即图结构
  3. 入度为0的节点入队,判断其所有能达到的节点,入度减1后,是否为0,为0则入队。
class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        // 生成图
        List<Integer>[] list = new ArrayList[numCourses];
        // 记录节点的入度
        int[] points = new int[numCourses];
        for (int[] p : prerequisites) {
            points[p[0]]++;  // [1, 0], 则0->1, 1入度++
            if (list[p[1]] == null) {
                list[p[1]] = new ArrayList<>();
            }
            list[p[1]].add(p[0]);  // 将0能到达的节点加入集合
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {  // 入度为0的节点加入队列
            if (points[i] == 0) {
                queue.offer(i);
            }
        }
        int[] res = new int[numCourses];  // 记录结果顺序
        int index = 0;
        while (!queue.isEmpty()) {
            int node = queue.poll();
            res[index++] = node;
            List<Integer> li = list[node];
            if (li == null) continue;
            for (int val : li) {
                points[val]--;
                if (points[val] == 0) {
                    queue.offer(val);
                }
            }
        }
        return index == numCourses ? res : new int[0];
    }
}

生成图可以用列表数组,也可以用嵌套列表,效果是一样的

// 生成图
// 用列表数组
List<Integer>[] list = new ArrayList[numCourses];
// 用嵌套列表
List<List<Integer>> list = new ArrayList<>();
207. 课程表

leetcode链接:207. 课程表
【Leetcode】图算法总结_第3张图片
BFS

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<Integer>[] list = new ArrayList[numCourses];
        int[] points = new int[numCourses];
        for (int[] p : prerequisites) {
            points[p[0]]++;
            if (list[p[1]] == null) list[p[1]] = new ArrayList<>();
            list[p[1]].add(p[0]);
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (points[i] == 0) {
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()) {
            int node = queue.poll();
            numCourses--;
            if (list[node] == null) continue;
            for (int val : list[node]) {
                points[val]--;
                if (points[val] == 0) {
                    queue.offer(val);
                }
            }
        }
        return numCourses == 0;
    }
}

DFS

通过 DFS 判断图中是否有环。有环则返回false。

算法流程:

  1. 借助一个标志列表 flags,用于判断每个节点 i (课程)的状态:

    1. 未被 DFS 访问:i == 0;
    2. 已被其他节点启动的 DFS 访问:i == -1;
    3. 已被当前节点启动的 DFS 访问:i == 1。
  2. 对 numCourses 个节点依次执行 DFS,判断每个节点起步 DFS 是否存在环,若存在环直接返回False。DFS 流程;

    1. 终止条件:
    • 当 flag[i] == -1,说明当前访问节点已被其他节点启动的 DFS 访问,无需再重复搜索,直接返回 True。
    • 当 flag[i] == 1,说明在本轮 DFS 搜索中节点 i 被第 2 次访问,即 课程安排图有环 ,直接返回 False。
    1. 将当前访问节点 i 对应 flag[i] 置 1,即标记其被本轮 DFS 访问过;
    2. 递归访问当前节点 i 的所有邻接节点 j,当发现环直接返回 False;
    3. 当前节点所有邻接节点已被遍历,并没有发现环,则将当前节点 flag 置为 −1-1−1 并返回 True。
  3. 若整个图 DFS 结束并未发现环,返回 True。

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<List<Integer>> list = new ArrayList<>();
        for(int i = 0; i < numCourses; i++) {
            list.add(new ArrayList<>());
        }
        int[] flags = new int[numCourses];
        for(int[] p : prerequisites) {
            list.get(p[1]).add(p[0]);
        }
        for(int i = 0; i < numCourses; i++) {
            if(!dfs(list, flags, i)) {
                return false;
            }
        }
        return true;
    }
    private boolean dfs(List<List<Integer>> list, int[] flags, int i) {
        if(flags[i] == 1) return false;
        if(flags[i] == -1) return true;
        flags[i] = 1;
        for(Integer j : list.get(i)) {
            if(!dfs(list, flags, j)) {
                return false;
            }
        }
        flags[i] = -1;
        return true;
    }
}

你可能感兴趣的:(算法分析,leetcode,图,Dijkstra,最短路径)