力扣经典图论题目打卡记录

文章目录

    • @[TOC](文章目录)
  • 前言
  • 一、力扣743. 网络延迟时间-单源最短路问题
    • 1.Dijkstra解法
    • 2.Floyd解法
  • 二、力扣133. 克隆图-DFS/BFS
  • 三、力扣210. 课程表 II-拓扑排序
  • 四、力扣310. 最小高度树-拓扑排序(度的概念)
  • 五、力扣329. 矩阵中的最长递增路径-记搜可以解(困难题)
  • 总结

前言

力扣经常看到评论区名言:简单题我重拳出击,中等题我努力思考,困难题我复制粘贴。
以前碰到力扣图论问题的时候好多都是直接放弃挣扎,被认定为困难题我铺好床垫,化身cv工程师。之前在春招和秋招的时候因为时间问题,最后一道图论也大多会放弃,专攻前面两道。
最近有更多一点的空闲时间,就打算把它捡起来,下定决心迎难而上,做几道高频专题先练练手,想着说不定能够总结出一些套路或者经典模板操作出来,避免“谈图色变”,下次碰到图相关的题目倒不至于直接投了。下面是近一周的经典高频题目打卡总结。


一、力扣743. 网络延迟时间-单源最短路问题

据说是少有的单源最短路直接套模板的题目。常见的模板解法包括Dijkstra和Floyd算法(目前还在接受能力范围之内)。
力扣经典图论题目打卡记录_第1张图片

1.Dijkstra解法

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        final int INF = Integer.MAX_VALUE / 2;
        int[][] graph = new int[n][n];
        boolean[] visited = new boolean[n];
        int[] dist = new int[n];
        Arrays.fill(dist, INF);
        for (int i = 0; i < n; i++) {
            Arrays.fill(graph[i], INF);
        }
        for (int[] time : times) {
            graph[time[0] - 1][time[1] - 1] = time[2];
        }
        // 更新k到其余各点的距离
        for (int i = 0; i < n; i++) { 
            dist[i] = graph[k - 1][i];
        }
        dist[k - 1] = 0;
        visited[k - 1] = true;
        // k节点已遍历,只需遍历剩余n-1个节点
        for (int i = 0; i < n - 1; i++) {
            int min = INF;
            int u = -1;
            // 找到未访问节点中距离最近的节点
            for (int j = 0; j < n; j++) {
                if (!visited[j] && dist[j] < min) {
                    min = dist[j];
                    u = j;
                }
            }
            // u=-1说明剩余的节点都不可达,直接返回-1
            if (u == -1) {
                return -1;
            }
            visited[u] = true;
            // 以u节点为准更新u到其余节点的距离
            for (int j = 0; j < n; j++) {
                // 如果k经过u到达j的距离小于k直接到j的距离,则更新dist,取最小的距离
                if (!visited[j] && graph[u][j] + dist[u] < dist[j]) {
                    dist[j] = graph[u][j] + dist[u];
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, dist[i]);
        }
        return ans == INF ? -1 : ans;
    }
}

其实还可以利用优先级队列进行优化。除了枚举,我们还可以使用一个小根堆来寻找「未确定节点」中与起点距离最近的点。

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        final int INF = Integer.MAX_VALUE / 2;
        List<int[]>[] g = new List[n];
        for (int i = 0; i < n; ++i) {
            g[i] = new ArrayList<int[]>();
        }
        for (int[] t : times) {
            int x = t[0] - 1, y = t[1] - 1;
            g[x].add(new int[]{y, t[2]});
        }

        int[] dist = new int[n];
        Arrays.fill(dist, INF);
        dist[k - 1] = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
        pq.offer(new int[]{0, k - 1});
        while (!pq.isEmpty()) {
            int[] p = pq.poll();
            int time = p[0], x = p[1];
            if (dist[x] < time) {
                continue;
            }
            for (int[] e : g[x]) {
                int y = e[0], d = dist[x] + e[1];
                if (d < dist[y]) {
                    dist[y] = d;
                    pq.offer(new int[]{d, y});
                }
            }
        }

        int ans = Arrays.stream(dist).max().getAsInt();
        return ans == INF ? -1 : ans;
    }
}

2.Floyd解法

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        final int INF = Integer.MAX_VALUE / 2;
        int[][] dist = new int[n][n];
        for (int i = 0; i < n; i++) {
            Arrays.fill(dist[i], INF);
        }
        for (int[] time : times) {
            dist[time[0] - 1][time[1] - 1] = time[2];
        }
        for (int v = 0; v < n; v++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][j] > dist[i][v] + dist[v][j]) {
                        dist[i][j] = dist[i][v] + dist[v][j];
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if (i != k - 1) {
                ans = Math.max(ans, dist[k - 1][i]);
            }
            
        }
        return ans == INF ? -1 : ans;
    }
}

二、力扣133. 克隆图-DFS/BFS

这个其实不难,用DFS或者BFS遍历拷贝就行。
力扣经典图论题目打卡记录_第2张图片

/*
// Definition for a Node.
class Node {
    public int val;
    public List neighbors;
    public Node() {
        val = 0;
        neighbors = new ArrayList();
    }
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList();
    }
    public Node(int _val, ArrayList _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/

class Solution {
    public Node cloneGraph(Node node) {
        if(node==null) return node;

        HashMap<Node,Node> visited = new HashMap<>();

        Queue<Node> que = new LinkedList<>();
        que.offer(node);
        visited.put(node,new Node(node.val,new ArrayList<>()));

        while(!que.isEmpty()){
            Node n = que.poll();
            for(Node neighbor:n.neighbors){
                if(!visited.containsKey(neighbor)){
                    Node cloneNode = new Node(neighbor.val,new ArrayList<>());
                    visited.put(neighbor,cloneNode);
                    que.offer(neighbor);
                }
                visited.get(n).neighbors.add(visited.get(neighbor));
            }
        }
        
        return visited.get(node);
    }
}

三、力扣210. 课程表 II-拓扑排序

本题是一道经典的「拓扑排序」问题。尽管官方给出了DFS和BFS的解法,但是都不如拓扑排序理解的更直接。
力扣经典图论题目打卡记录_第3张图片

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] dig = new int[numCourses];
        for(int[] prerequisite:prerequisites){
            dig[prerequisite[0]]++;
        }

        Queue<Integer> que = new LinkedList<>();
        for(int i=0;i<dig.length;i++){
            if(dig[i]==0) que.offer(i);
        }

        int[] res = new int[numCourses];
        int count = 0;

        while(!que.isEmpty()){
            int code = que.poll();
            res[count++] = code;
            for(int[] prerequisite:prerequisites){
                if(prerequisite[1]==code){
                    dig[prerequisite[0]]--;
                    if(dig[prerequisite[0]]==0){
                        que.offer(prerequisite[0]);
                    }
                }
            }

        }
        if(count==numCourses) return res;
        return new int[0];
    }
}

四、力扣310. 最小高度树-拓扑排序(度的概念)

还是拓扑排序,加入度的概念更好理解。
力扣经典图论题目打卡记录_第4张图片

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        //最终输出结果集
        List<Integer> res = new ArrayList<Integer>();
        if(n==1){
            res.add(0);
            return res;
        }

        //定义度 & 建图
        int[] degree = new int[n];
        //边的集合
        List<Integer>[] adj = new List[n];
        for(int i=0;i<n;i++){
            adj[i] = new ArrayList<Integer>();
        }
        for(int[] edge:edges){
            adj[edge[0]].add(edge[1]);
            adj[edge[1]].add(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }

        //所有入度为1的节点 入队列
        Queue<Integer> que = new ArrayDeque<Integer>();
        for(int i=0;i<n;i++){
            if(degree[i]==1){
                que.offer(i);
            }
        }

        //出队 直到节点数剩余<=2
        int remain = n;
        while(remain>2){
            int size = que.size();
            remain-=size;
            for(int i=0;i<size;i++){
                int id = que.poll();
                for(int node: adj[id]){
                    degree[node]--;
                    if(degree[node]==1){
                        que.offer(node);
                    }
                }
            }
        }

        //合并结果
        while(!que.isEmpty()){
            res.add(que.poll());
        }

        return res;
    }
}

五、力扣329. 矩阵中的最长递增路径-记搜可以解(困难题)

不一定要用拓扑排序,记忆化搜索可以解决,将矩阵看成一个有向图,每个单元格对应图中的一个节点,如果相邻的两个单元格的值不相等,则在相邻的两个单元格之间存在一条从较小值指向较大值的有向边。问题转化成在有向图中寻找最长路径。
力扣经典图论题目打卡记录_第5张图片

class Solution {
    public int longestIncreasingPath(int[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        if(matrix==null || n==0 || m==0) return 0;
        int res = 0;
        int[][] memo = new int[n][m];
        int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
        for(int i=0;i<n;i++){
            for(int j= 0;j<m;j++){
                res = Math.max(res,dfs(matrix,i,j,memo,dirs));
            }
        }
        return res;
    }

    public int dfs(int[][] matrix,int i,int j, int[][] memo,int[][] dirs){
        if(memo[i][j]!=0) return memo[i][j];
        memo[i][j]++;
        
        for(int[] dir:dirs){
            int newX = i+dir[0];
            int newY = j+dir[1];
            if(newX>=0&&newX<matrix.length&&newY>=0&&newY<matrix[0].length&&matrix[newX][newY]>matrix[i][j])
            memo[i][j] = Math.max(memo[i][j],dfs(matrix,newX,newY,memo,dirs)+1);
        }

        return memo[i][j];
    }
}

总结

本文总结了最近一周自己刷的图论相关的题目,包括有向/无向无环图的dfs和bfs 、拓扑排序、单源最短路Dijkstra和Floyd算法等,之前总是想放弃的,现在我考虑正面面对它。

你可能感兴趣的:(java,图论)