LeetCode743: 网络延迟问题(Java)

如题: image-20220514183856067

分析思路:

  • 从实例的途中就可以看出这是一个有向带权图,每条边的权重表示从一个节点发送信号到另一个节点所需要的时间。

  • 题目要我们返回从某一个节点开始发送信号,需要多久才能使所有节点都收到这个信号,这里有两层含义

    • 第一层:如果当前节点到另一个节点有两条路径,这两条路径都能到达,我们应该选取花费时间更少的那条
    • 第二层:需要保证所有节点都能收到该信号,因此所需要的时间应为当前节点到其他节点的需要花费时间中需要时间最久的那个时间,这样才能保证在这个时间内所有节点都能收到该信号
  • 因此,第一个问题是如何得到当前节点到另一个节点的所有路径中花费时间最短的路径?

    • 这个问题我们可以用dijkstra算法解决,这个算法就是解决最短路径问题的。
    • 注意dijkstra算法的使用条件:加权有向图,没有负权重,因此当前问题满足条件。
  • 第二个问题是如何知道到哪个节点所需要的 最短路径时间 是最长的?

    • 我们可以在dijkstra算法计算每一个节点的耗费时间的同时利用一个数组记录当前节点到每一个节点所需要的最短时间。

代码:

  • dijkstra算法实质上是在BFS层次遍历图的过程中计算最短路径的,因此我们需要构建图,这里用邻接表法构建图。
  • dijkstra需要一个数组记录开始结点到其他所有节点的最短路径长度,我们可以这样实现:
    • 初始化一个distFromStart,记录的值初始化为无穷大(因为我们要缩小这个值)。
    • 在遍历图的过程中找到当前结点的所有邻居节点,然后计算从开始结点到当前结点的路径长度加上当前节点到邻居节点的权重,即当前路径到该达邻居节点的路径长度,判断这个长度和distFromStart数组记录的长度的大小,distFromStart数组始终记录更小的那个值。
      • 上述条件中我们可以知道的是当前结点到邻居节点的权重,那么我们如何知道开始节点到当前节点的路径长度?
        • 我们可以用一个类来记录这个信息,这个类有属性id表示节点编号,属性distFromStart表示开始结点到当前结点的路径长度。在BFS过程中我们用这个类的对象来代表每一个节点,加入到队列中遍历。
    • 这样我们在遍历的时候弹出队列中的节点就可以得到开始结点到当前结点的路径长度了,在利用建立的邻接表可以根据这个节点的id找到它的每一个邻居,由此可以完成判断。
class Solution {
    //表里每一个节点的类型是int[],该数组长度为2,0索引存放目的节点,1索引存放到目的节点的权重。
    List<int[]>[] graph;
    //实时记录每个结点到起始结点的最短距离,在每次寻找邻居节点的时候用来判断当前路径走这条路是否可行
    int[] distFromStart;


    public int networkDelayTime(int[][] times, int n, int k) {
        //用邻接表法建图
        built(times, n);
        //用dijkstra计算每个结点和起始结点的最短路径
        dijkstra(graph, k);
        //找最大时间,代表所有结点收到信号的最短时间
        int maxTime = 0;
        //从1开始,结点编号没有0
        for (int i = 1; i < distFromStart.length; i++) {
        //如果长度还是101则表示路径长度没有更新,即表示这个节点无法到达,直接返回-1
            if (distFromStart[i] == 101) return -1;
            if (distFromStart[i] > maxTime) maxTime = distFromStart[i];
        }
        return maxTime;
    }

    void built(int[][] times, int n) {
        //由于节点的编号是从1开始的,所以初始化大小n+1
        graph = new ArrayList[n + 1];
        for (int i = 0; i < n + 1; i++) {
            graph[i] = new ArrayList<>();
        }
        for (int[] time : times) {
            int start = time[0];
            int to = time[1];
            int weight = time[2];
            graph[start].add(new int[]{to, weight});
        }
    }

    //记录每个几点的基本信息,即编号和与起始结点的路径长度
    static class Node {
        int id;
        int distFromStart;

        public Node(int id, int distFromStart) {
            this.id = id;
            this.distFromStart = distFromStart;
        }
    }

    public void dijkstra(List<int[]>[] graph, int start) {
        //优先权队列对 从开始结点到当前结点的距离distFromStart 从小到大排序
        PriorityQueue<Node> priorityQueue = new PriorityQueue<>((a, b) -> a.distFromStart - b.distFromStart);

        distFromStart = new int[graph.length];
        //权重最大为100,初始化为一个大于它的数
        Arrays.fill(distFromStart, 101);
        distFromStart[start] = 0;

        priorityQueue.offer(new Node(start, 0));

        while (!priorityQueue.isEmpty()) {
            Node Node = priorityQueue.poll();
            int curNodeID = Node.id;//当前结点id
            int curDistFromStart = Node.distFromStart;//当前路径下开始节点到当前结点的路径长度
            
            if (curDistFromStart > distFromStart[curNodeID]) {
                // 表示已经有一条更短的路径到达 curNode 节点了
                continue;
            }
            
            for (int[] neighbor : graph[curNodeID]) {
                int weight = neighbor[1];
                int nextNodeID = neighbor[0];
                if (curDistFromStart + weight < distFromStart[nextNodeID]) {
                    distFromStart[nextNodeID] = curDistFromStart + weight;
                    priorityQueue.offer(new Node(nextNodeID, curDistFromStart + weight));
                }
            }
        }
    }
}
  • 上面我们使用优先权队列保存每个而不是普通队列,这样效率更高。因为我们要找的是开始节点到当前节点的最短路径,在找这条最短路径的过程中我们如果能够保证每次都从更短的子路径开始找,则最终的找到的路径是最短路径的概率会更高。这是一种贪心策略。
  • BFS遍历图的时候我们不需要使用visited数组保证我们不走回头路,因为我们只有在找到更短路径的时候才回把节点加入队列中,如果走回头路的话这个路径肯定比数组中记录的更长。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
打开leetcode很卡 Leetcode | 算法刷题 算法学习: 由于Leetcode题量大,内容多,复习难度大,学习掌握算法的情况也不是很好。 从8.28开始转刷剑指offer,一共67道题目,其中简单题7道,中等难度题31道,较难题26道,困难题3道 1: 简单题和中等难度题(共38道题目) 2: 最优解,做笔记 3: 每天2道,隔天复习(即每天4道题) 4: Python 每日打卡: 8月: 28:1⃣剑指offer刷题 29:1⃣剑指offer刷题 30:1⃣剑指offer刷题2⃣博客网站布局搭建完毕 8月 28 29 30 31 . . . 1h练字/阅读 :check_box_with_check: :check_box_with_check: :check_box_with_check: :check_box_with_check: :cross_mark: :cross_mark: :cross_mark: 2+2算法题 :check_box_with_check: :check_box_with_check: :check_box_with_check: :check_box_with_check: :cross_mark: :cross_mark: :cross_mark: 博客网站 :check_box_with_check: :check_box_with_check: :check_box_with_check: :check_box_with_check: :cross_mark: :cross_mark: :cross_mark: 毕业课题 :cross_mark: :cross_mark: :check_box_with_check: :check_box_with_check: :cross_mark: :cross_mark: :cross_mark: 认真投入学习 9月第一周 1 2 3 4 5 6 7 1h练字/阅读 :check_box_with_check: :check_box_with_check: :cross_mark: :check_box_with_check: - - - 2+2算法题 :check_box_with_check: :check_box_with_check: :cross_mark: :check_box_with_check: - - - 博客网站 :check_box_with_check: :check_box_with_check: :check_box_with_check: :check_box_with_check: :cross_mark: :cross_mark: :cross_mark: 毕业课题 :cross_mark: :cross_mark: :cross_mark: :cross_mark: :cross_mark: :cross_mark: :cross_mark: 1号:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一酒。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值