news 2026/4/23 19:22:13

算法题 访问所有节点的最短路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 访问所有节点的最短路径

847. 访问所有节点的最短路径

问题描述

给你一个无向连通图,包含n个节点,编号从0n-1。给你一个二维数组graph,其中graph[i]是与节点i相连的节点列表。

返回访问所有节点的最短路径长度。你可以从任意节点开始和结束,可以多次访问同一节点和边。

注意

  • 图是连通的(任意两个节点之间都有路径)
  • 节点数1 <= n <= 12
  • 可以重复访问节点和边

示例

输入:graph=[[1,2,3],[0],[0],[0]]输出:4解释:一种最短路径是[1,0,2,0,3]
输入:graph=[[1],[0,2,4],[1,3,4],[2],[1,2]]输出:4解释:一种最短路径是[0,1,4,2,3]

算法思路

状态压缩BFS

  1. 核心

    • 带状态的最短路径问题
    • 状态 = (当前节点, 已访问的节点集合)
    • 由于 n ≤ 12,可以用位掩码表示已访问的节点集合
  2. 状态

    • 使用整数mask表示已访问的节点集合
    • i位为1表示节点i已访问
    • 状态 =(node, mask)
  3. BFS

    • 从每个节点作为起点开始BFS
    • 队列存储(当前节点, 访问状态, 路径长度)
    • 目标状态:mask == (1 << n) - 1(所有节点都已访问)
  4. 去重

    • 使用visited[node][mask]避免重复访问相同状态
    • 求最短路径,第一次到达某个状态就是最优的
  5. 为什么用BFS而不是DFS?
    BFS保证第一次到达目标状态时路径最短。

代码实现

importjava.util.*;classSolution{/** * 计算访问所有节点的最短路径长度 * 使用状态压缩BFS,状态 = (当前节点, 已访问节点的位掩码) * * @param graph 无向连通图的邻接表 * @return 访问所有节点的最短路径长度 */publicintshortestPathLength(int[][]graph){intn=graph.length;// 特殊情况:只有一个节点if(n==1){return0;}// 目标状态:所有n个节点都被访问inttargetMask=(1<<n)-1;// visited[node][mask] 表示是否已经访问过状态(node, mask)boolean[][]visited=newboolean[n][1<<n];// BFS队列:存储 [当前节点, 访问状态, 路径长度]Queue<int[]>queue=newLinkedList<>();// 初始化:从每个节点开始BFSfor(inti=0;i<n;i++){intstartMask=1<<i;// 只访问了节点iqueue.offer(newint[]{i,startMask,0});visited[i][startMask]=true;}// BFS搜索while(!queue.isEmpty()){int[]current=queue.poll();intnode=current[0];intmask=current[1];intdistance=current[2];// 遍历当前节点的所有邻居for(intneighbor:graph[node]){// 更新访问状态:将neighbor标记为已访问intnewMask=mask|(1<<neighbor);// 检查是否达到目标状态if(newMask==targetMask){returndistance+1;}// 如果状态未访问过,加入队列if(!visited[neighbor][newMask]){visited[neighbor][newMask]=true;queue.offer(newint[]{neighbor,newMask,distance+1});}}}return-1;}}

算法分析

  • 时间复杂度:O(n² × 2ⁿ)
    • 状态数量:n个节点 × 2ⁿ种访问状态 = O(n × 2ⁿ)
    • 每个状态需要遍历其所有邻居,最多n个邻居
    • 总体:O(n² × 2ⁿ)
  • 空间复杂度:O(n × 2ⁿ)
    • visited数组大小:n × 2ⁿ
    • BFS队列最多存储 O(n × 2ⁿ) 个状态

算法过程

1:graph = [[1,2,3],[0],[0],[0]]

图结构

1 | 0 / \ 2 3

BFS

  1. 初始状态(距离0):

    • (0, 0001),(1, 0010),(2, 0100),(3, 1000)
  2. 距离1

    • 从0:(1, 0011),(2, 0101),(3, 1001)
    • 从1:(0, 0011)
    • 从2:(0, 0101)
    • 从3:(0, 1001)
  3. 距离2

    • 从(1,0011):(0,0011)(已访问)
    • 从(2,0101):(0,0101)(已访问)
    • 从(3,1001):(0,1001)(已访问)
    • 从(0,0011):(2,0111),(3,1011)
    • 从(0,0101):(1,0111),(3,1101)
    • 从(0,1001):(1,1011),(2,1101)
  4. 距离3

    • 从(2,0111):(0,0111)(已访问)
    • 从(3,1011):(0,1011)(已访问)
    • 从(1,0111):(0,0111)(已访问)
    • 从(3,1101):(0,1101)(已访问)
    • 从(1,1011):(0,1011)(已访问)
    • 从(2,1101):(0,1101)(已访问)
  5. 距离4

    • 从(0,0111):(3,1111)目标状态!返回4
    • 从(0,1011):(2,1111)目标状态!返回4
    • 从(0,1101):(1,1111)目标状态!返回4

结果:4

测试用例

publicclassMain{publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:星形图int[][]graph1={{1,2,3},{0},{0},{0}};System.out.println("Test 1: "+solution.shortestPathLength(graph1));// 4// 测试用例2:复杂图int[][]graph2={{1},{0,2,4},{1,3,4},{2},{1,2}};System.out.println("Test 2: "+solution.shortestPathLength(graph2));// 4// 测试用例3:两个节点int[][]graph3={{1},{0}};System.out.println("Test 3: "+solution.shortestPathLength(graph3));// 1// 测试用例4:三个节点线性int[][]graph4={{1},{0,2},{1}};System.out.println("Test 4: "+solution.shortestPathLength(graph4));// 2// 测试用例5:完全图(3个节点)int[][]graph5={{1,2},{0,2},{0,1}};System.out.println("Test 5: "+solution.shortestPathLength(graph5));// 2// 测试用例6:单节点int[][]graph6={{}};System.out.println("Test 6: "+solution.shortestPathLength(graph6));// 0// 测试用例7:四个节点环int[][]graph7={{1,3},{0,2},{1,3},{0,2}};System.out.println("Test 7: "+solution.shortestPathLength(graph7));// 3// 测试用例8:链状图(4个节点)int[][]graph8={{1},{0,2},{1,3},{2}};System.out.println("Test 8: "+solution.shortestPathLength(graph8));// 3// 测试用例9:复杂连通图int[][]graph9={{1,2,3,4},{0,2,3,4},{0,1,3,4},{0,1,2,4},{0,1,2,3}};System.out.println("Test 9: "+solution.shortestPathLength(graph9));// 2}}

关键点

  1. 状态压缩

    • 用位掩码表示集合处理"访问过哪些节点"问题
  2. 多源BFS

    • 可以从任意节点开始,需要将所有节点作为起点
  3. 状态去重

    • 同一状态(node, mask)只需要访问一次
    • 重复访问不会产生更短的路径

常见问题

  1. 为什么不用Dijkstra算法?
    所有边的权重都是1,BFS就是最短路径算法。Dijkstra适用于带权重的图。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 23:46:51

生态数据分析完全指南:vegan R包从入门到精通

生态数据分析完全指南&#xff1a;vegan R包从入门到精通 【免费下载链接】vegan R package for community ecologists: popular ordination methods, ecological null models & diversity analysis 项目地址: https://gitcode.com/gh_mirrors/ve/vegan 生态数据分析…

作者头像 李华
网站建设 2026/4/23 6:57:46

一次性客户行项目的地址与税号从哪里来:基于 CDS 视图 I_OneTimeAccountCustomer 的数据建模与实战用法

在不少财务场景里,你会遇到一种看起来像客户、又不像客户的对象:系统里明明挂着一个客户号,但这个客户号更像一个公共马甲,只用于过账;真正的客户名称、地址、税号等信息,是在录入凭证时临时填进去的。典型例子是展会现场的散客、一次性合作的临时客户、仅发生一次收款的…

作者头像 李华
网站建设 2026/4/23 12:52:22

Dify平台允许自定义评分机制评估生成结果

Dify平台允许自定义评分机制评估生成结果 在企业级AI应用日益普及的今天&#xff0c;一个核心问题逐渐浮现&#xff1a;我们如何信任大语言模型&#xff08;LLM&#xff09;的每一次输出&#xff1f;当智能客服回复客户账单疑问、法律助手起草合同条款、或医疗系统生成诊断建议…

作者头像 李华
网站建设 2026/4/23 14:35:46

为什么顶尖AI团队都在关注Open-AutoGLM?(5个关键优势曝光)

第一章&#xff1a;为什么顶尖AI团队都在关注Open-AutoGLM&#xff1f;在生成式AI快速演进的今天&#xff0c;自动化语言模型&#xff08;AutoGLM&#xff09;正成为提升大模型研发效率的关键技术。Open-AutoGLM作为首个开源的全自动类GPT模型训练框架&#xff0c;因其高度模块…

作者头像 李华
网站建设 2026/4/23 14:46:09

比Open-AutoGLM更强的,是如何实现零样本超收敛的?

第一章&#xff1a;比Open-AutoGLM更强的在当前自动化代码生成与智能编程辅助工具快速演进的背景下&#xff0c;新一代模型正在突破Open-AutoGLM的能力边界。这些新架构不仅在代码理解深度上表现更优&#xff0c;还在多语言支持、上下文推理和跨项目迁移能力方面实现了显著提升…

作者头像 李华
网站建设 2026/4/23 14:01:04

3分钟解锁联想拯救者BIOS隐藏功能:性能优化完全指南

还在为BIOS设置选项有限而苦恼吗&#xff1f;联想拯救者系列笔记本默认隐藏了大量高级设置&#xff0c;限制了用户对硬件性能的充分挖掘。这款专为Insyde BIOS设计的解锁工具&#xff0c;让你在3分钟内轻松开启所有隐藏功能&#xff0c;实现真正的个性化配置。 【免费下载链接】…

作者头像 李华