news 2026/6/9 18:52:32

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

作者头像

张小明

前端开发工程师

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/6/5 9:23:38

Open-AutoGLM模型Git地址曝光:如何获取最新版本并参与社区贡献

第一章&#xff1a;Open-AutoGLM模型git地址Open-AutoGLM 是一个开源的自动化图学习模型框架&#xff0c;旨在简化图神经网络&#xff08;GNN&#xff09;在复杂场景下的建模与训练流程。该项目由社区驱动&#xff0c;支持多种图结构数据的自动特征工程、模型选择与超参优化。项…

作者头像 李华
网站建设 2026/6/9 18:51:29

从0到1构建AutoGLM系统,手把手教你掌握智谱大模型自动化架构

第一章&#xff1a;AutoGLM系统概述与核心理念AutoGLM 是一个面向自动化自然语言处理任务的智能系统框架&#xff0c;旨在通过大语言模型&#xff08;LLM&#xff09;驱动的工作流实现从数据输入到结果输出的端到端自动化。其核心设计理念是“智能代理 可编排流程”&#xff0…

作者头像 李华
网站建设 2026/6/9 18:52:35

【专家级教程】:Open-AutoGLM容器化部署实战,Docker+K8s双环境详解

第一章&#xff1a;智谱开源Open-AutoGLM部署概述Open-AutoGLM 是智谱AI推出的一款面向自动化图学习任务的开源框架&#xff0c;旨在简化图神经网络&#xff08;GNN&#xff09;在复杂场景下的建模流程。该框架集成了自动特征工程、模型选择与超参优化能力&#xff0c;支持多种…

作者头像 李华
网站建设 2026/6/5 14:27:41

【AI自动化新突破】:Open-AutoGLM在Ubuntu虚拟机上的10个关键优化点

第一章&#xff1a;Open-AutoGLM与Ubuntu虚拟机集成概述Open-AutoGLM 是一个基于开源大语言模型&#xff08;LLM&#xff09;的自动化代码生成框架&#xff0c;具备自然语言到代码的高效转换能力。将其部署于 Ubuntu 虚拟机中&#xff0c;不仅可以实现资源隔离与环境标准化&…

作者头像 李华
网站建设 2026/6/5 10:39:43

一文吃透 I_FunctionalLocation:用 CDS 视图把功能位置主数据变成可复用的数据底座

在设备管理与工厂维护领域里,Functional Location(功能位置)几乎是所有分析、报表、接口、移动应用绕不开的主数据之一。它把复杂的技术资产按层级组织起来:一栋楼、一条产线、一套装置、一个工段……都可以被建模为功能位置,并在其下安装设备、记录维修、归集成本、追溯责…

作者头像 李华
网站建设 2026/6/9 8:22:56

为什么你的Open-AutoGLM跑不起来?Ubuntu虚拟机配置常见问题全解析

第一章&#xff1a;Open-AutoGLM在Ubuntu虚拟机中的运行困境在尝试于Ubuntu虚拟机环境中部署和运行Open-AutoGLM时&#xff0c;开发者常遭遇一系列与环境依赖、资源分配及权限配置相关的挑战。这些问题不仅影响模型的启动效率&#xff0c;还可能导致推理过程中的不可预测中断。…

作者头像 李华