news 2026/9/23 16:01:07

算法题 网络延迟时间

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 网络延迟时间

网络延迟时间

问题描述

n个网络节点,标记为1n
给你一个列表times,表示信号经过有向边的传递时间:times[i] = (ui, vi, wi),其中ui是源节点,vi是目标节点,wi是一个信号从源节点传递到目标节点的时间。

现在,从某个节点k发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回-1

示例

输入: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2 输出: 2

算法思路

经典的单源最短路径问题,可以使用Dijkstra算法解决。

Dijkstra算法核心思想

  1. 维护一个距离数组dist,记录从起始节点到每个节点的最短距离
  2. 使用优先队列(最小堆)选择当前距离最短的未处理节点
  3. 对选中的节点,更新其邻居节点的距离
  4. 重复直到处理完所有可达节点

关键

  1. 建图:将输入的边列表转换为邻接表表示
  2. 初始化:起始节点距离为0,其他节点距离为无穷大
  3. 松弛操作:通过当前节点更新邻居节点的最短距离
  4. 结果计算:找到所有节点中的最大距离,如果有节点不可达则返回-1

代码实现

方法一:Dijkstra算法

importjava.util.*;classSolution{/** * 计算从节点k发出信号到达所有节点所需的最长时间 * * @param times 有向边列表,每个元素为[源节点, 目标节点, 权重] * @param n 网络节点总数(节点编号1到n) * @param k 信号起始节点 * @return 所有节点收到信号的最短时间,如果无法到达所有节点返回-1 */publicintnetworkDelayTime(int[][]times,intn,intk){// 1: 构建邻接表表示的图// graph[i] 存储从节点i出发的所有边,每个边用[目标节点, 权重]表示List<int[]>[]graph=newList[n+1];for(inti=1;i<=n;i++){graph[i]=newArrayList<>();}// 填充邻接表for(int[]edge:times){intu=edge[0],v=edge[1],w=edge[2];graph[u].add(newint[]{v,w});}// 2: 初始化距离数组// dist[i] 表示从起始节点k到节点i的最短距离int[]dist=newint[n+1];Arrays.fill(dist,Integer.MAX_VALUE);dist[k]=0;// 起始节点到自身的距离为0// 3: 使用优先队列实现Dijkstra算法// 优先队列存储[节点, 距离],按距离从小到大排序PriorityQueue<int[]>pq=newPriorityQueue<>((a,b)->a[1]-b[1]);pq.offer(newint[]{k,0});// 记录已处理的节点数量boolean[]visited=newboolean[n+1];while(!pq.isEmpty()){int[]current=pq.poll();intnode=current[0];intdistance=current[1];// 如果当前节点已经处理过,跳过(处理重复入队的情况)if(visited[node]){continue;}visited[node]=true;// 遍历当前节点的所有邻居for(int[]neighbor:graph[node]){intnextNode=neighbor[0];intweight=neighbor[1];// 如果通过当前节点到达邻居的距离更短,则更新if(dist[node]+weight<dist[nextNode]){dist[nextNode]=dist[node]+weight;pq.offer(newint[]{nextNode,dist[nextNode]});}}}// 4: 计算结果intmaxTime=0;for(inti=1;i<=n;i++){// 如果存在节点不可达,返回-1if(dist[i]==Integer.MAX_VALUE){return-1;}maxTime=Math.max(maxTime,dist[i]);}returnmaxTime;}}

算法分析

  • 时间复杂度:O((V + E) log V)

    • V = n(节点数),E = times.length(边数)
    • 每个节点最多入队一次,每次堆操作O(log V)
    • 每条边最多被处理一次
  • 空间复杂度:O(V + E)

    • 邻接表存储:O(V + E)
    • 距离数组:O(V)
    • 优先队列:O(V)

算法过程

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2

初始化

  • 邻接表:graph[2] = [[1,1], [3,1]],graph[3] = [[4,1]]
  • 距离数组:dist = [∞, ∞, 0, ∞, ∞](索引0不使用)
  • 优先队列:[(2, 0)]

执行过程

  1. 处理节点2(距离0):

    • 更新节点1:dist[1] = 0 + 1 = 1
    • 更新节点3:dist[3] = 0 + 1 = 1
    • 队列:[(1,1), (3,1)]
  2. 处理节点1(距离1):

    • 节点1无出边
    • 队列:[(3,1)]
  3. 处理节点3(距离1):

    • 更新节点4:dist[4] = 1 + 1 = 2
    • 队列:[(4,2)]
  4. 处理节点4(距离2):

    • 节点4无出边
    • 队列为空

最终距离dist = [∞, 1, 0, 1, 2]
最大距离max(1, 0, 1, 2) = 2

测试用例

publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例int[][]times1={{2,1,1},{2,3,1},{3,4,1}};System.out.println("Test 1: "+solution.networkDelayTime(times1,4,2));// 2// 测试用例2:无法到达所有节点int[][]times2={{1,2,1}};System.out.println("Test 2: "+solution.networkDelayTime(times2,2,2));// -1// 测试用例3:单个节点int[][]times3={};System.out.println("Test 3: "+solution.networkDelayTime(times3,1,1));// 0// 测试用例4:复杂网络int[][]times4={{1,2,1},{2,3,2},{1,3,4}};System.out.println("Test 4: "+solution.networkDelayTime(times4,3,1));// 3// 测试用例5:包含环路但不影响最短路径int[][]times5={{1,2,1},{2,1,3},{2,3,2},{3,4,1}};System.out.println("Test 5: "+solution.networkDelayTime(times5,4,1));// 4// 测试用例6:大权重边int[][]times6={{1,2,100},{2,3,100},{3,4,100}};System.out.println("Test 6: "+solution.networkDelayTime(times6,4,1));// 300// 测试用例7:多条路径到同一节点int[][]times7={{1,2,1},{1,3,4},{2,3,2},{2,4,6},{3,4,3}};System.out.println("Test 7: "+solution.networkDelayTime(times7,4,1));// 6}

关键点

    • 使用邻接表而非邻接矩阵,节省空间
    • 邻接表索引从1开始,对应节点编号
  1. 优先队列

    • 始终选择当前距离最短的未处理节点
    • 保证每次处理的都是最优解(贪心策略)
  2. 重复入队处理

    • 同一节点可能多次入队(距离更新时)
    • 通过visited数组或距离比较避免重复处理
  3. 不可达节点

    • 距离仍为Integer.MAX_VALUE表示不可达
    • 只要有1个不可达节点就返回-1
  4. 结果

    • 需要所有节点都收到信号,所以取最大距离
    • 起始节点自身距离为0,不影响结果

常见问题

  1. 为什么使用Dijkstra而不是其他最短路径算法?

    • 传递时间都是正数,Dijkstra最适合
    • 如果有权重为负的情况,需要使用Bellman-Ford
  2. visited数组?

    • 使用visited数组可以减少堆操作次数,提高效率
  3. 如何处理节点编号从1开始?

    • 创建大小为n+1的数组,忽略索引0
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 12:30:08

深度解析 Flutter 自定义组件封装:从基础封装到高性能复用

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)&#xff0c;一起共建开源鸿蒙跨平台生态。在 Flutter 开发中&#xff0c;“组件化” 是提升开发效率、保证代码可维护性的核心抓手。原生组件虽能满足基础需求&#xff0c;但实际业务中&am…

作者头像 李华
网站建设 2026/9/24 5:02:33

顺序栈的入栈函数

顺序栈的知识&#xff1a; 参考视频 46:31-1:01:06这部分讲了栈的概念&#xff0c;顺序表的初始化&#xff0c;出栈&#xff0c;入栈&#xff0c;获取栈顶元素 https://www.bilibili.com/video/BV1tNpbekEht?t2790.6&p5 笔记&#xff1a; 栈和队列栈&#xff1a;只能…

作者头像 李华
网站建设 2026/9/24 5:23:21

利用清华镜像站高速下载GPT-OSS-20B模型权重文件

利用清华镜像站高速下载GPT-OSS-20B模型权重文件 在大语言模型迅速演进的今天&#xff0c;越来越多的研究者和开发者面临一个现实问题&#xff1a;如何在不依赖昂贵算力集群的前提下&#xff0c;本地部署并高效运行具备专业能力的大模型&#xff1f;答案正逐渐清晰——轻量级开…

作者头像 李华
网站建设 2026/9/23 12:11:33

告别低效推理!vLLM镜像助力企业级LLM生产部署

告别低效推理&#xff01;vLLM镜像助力企业级LLM生产部署 在今天的大模型应用浪潮中&#xff0c;越来越多的企业开始将大语言模型&#xff08;LLM&#xff09;嵌入到智能客服、内容生成、代码辅助等核心业务场景。然而&#xff0c;当理想照进现实——从实验室demo走向高并发、7…

作者头像 李华
网站建设 2026/9/21 20:40:33

英文长字符串不换行?前端开发者必备的CSS断行实战指南

英文长字符串不换行&#xff1f;前端开发者必备的CSS断行实战指南英文长字符串不换行&#xff1f;前端开发者必备的CSS断行实战指南当 URL 像火车一样冲出屏幕浏览器心里的小剧场&#xff1a;为啥不换&#xff1f;word-break 与 overflow-wrap&#xff1a;看似双胞胎&#xff0…

作者头像 李华
网站建设 2026/9/23 11:47:11

HunyuanVideo-Foley模型部署指南:Windows18-HD19环境下的安装包配置

HunyuanVideo-Foley模型部署实践&#xff1a;基于Windows18-HD19环境的完整配置与优化 在短视频创作井喷、影视工业化加速的今天&#xff0c;音效制作正面临前所未有的效率瓶颈。传统流程中&#xff0c;一个10秒的视频可能需要音效师手动匹配数个素材文件&#xff0c;并反复调整…

作者头像 李华