news 2026/5/8 17:00:04

算法题 所有可能的路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题 所有可能的路径

所有可能的路径

问题描述

给你一个有n个节点的有向无环图(DAG),节点编号从0n - 1。给你一个二维数组graph表示图的邻接表,其中graph[i]是一个节点数组,表示从节点i出发可以到达的所有节点。

请你找出从节点 0 到节点 n-1 的所有路径,并返回这些路径。

注意:图中不包含自环和平行边,且保证是无环图。

示例

输入: graph = [[1,2],[3],[3],[]] 输出: [[0,1,3],[0,2,3]] 解释: 从0到3有两条路径: 0->1->3 和 0->2->3。

算法思路

经典的图遍历问题

核心

  1. 有向无环图(DAG):保证不会有无限递归,每条路径都是有限的
  2. 路径记录:需要记录从起点到当前节点的完整路径
  3. 回溯算法:到达终点时保存路径,然后回溯继续探索其他路径

方法

  • 深度优先搜索(DFS) + 回溯
  • 广度优先搜索(BFS)
  • 动态规划:由于需要返回所有路径而不是计数,不适合

代码实现

方法一:DFS + 回溯

importjava.util.*;classSolution{/** * 使用DFS和回溯找到从0到n-1的所有路径 * * @param graph 邻接表表示的有向无环图 * @return 所有从0到n-1的路径列表 */publicList<List<Integer>>allPathsSourceTarget(int[][]graph){List<List<Integer>>result=newArrayList<>();List<Integer>path=newArrayList<>();// 从节点0开始DFSdfs(graph,0,path,result);returnresult;}/** * DFS回溯函数 * * @param graph 邻接表 * @param node 当前节点 * @param path 当前路径 * @param result 所有路径的结果列表 */privatevoiddfs(int[][]graph,intnode,List<Integer>path,List<List<Integer>>result){// 将当前节点加入路径path.add(node);// 如果到达目标节点(n-1)if(node==graph.length-1){// 保存当前路径的副本result.add(newArrayList<>(path));}else{// 递归访问所有邻居节点for(intneighbor:graph[node]){dfs(graph,neighbor,path,result);}}// 回溯:移除当前节点,返回上一层path.remove(path.size()-1);}}

方法二:BFS(广度优先搜索)

importjava.util.*;classSolution{/** * 使用BFS找到所有路径 * * @param graph 邻接表 * @return 所有路径 */publicList<List<Integer>>allPathsSourceTarget(int[][]graph){List<List<Integer>>result=newArrayList<>();inttarget=graph.length-1;// 队列存储路径,而不是单个节点Queue<List<Integer>>queue=newLinkedList<>();queue.offer(Arrays.asList(0));// 初始路径只包含起点0while(!queue.isEmpty()){List<Integer>currentPath=queue.poll();intlastNode=currentPath.get(currentPath.size()-1);// 如果到达目标节点if(lastNode==target){result.add(currentPath);}else{// 扩展所有可能的下一步for(intneighbor:graph[lastNode]){List<Integer>newPath=newArrayList<>(currentPath);newPath.add(neighbor);queue.offer(newPath);}}}returnresult;}}

方法三:迭代DFS

importjava.util.*;classSolution{/** * 使用显式栈实现的迭代DFS */publicList<List<Integer>>allPathsSourceTarget(int[][]graph){List<List<Integer>>result=newArrayList<>();inttarget=graph.length-1;// 栈存储路径Stack<List<Integer>>stack=newStack<>();stack.push(Arrays.asList(0));while(!stack.isEmpty()){List<Integer>currentPath=stack.pop();intlastNode=currentPath.get(currentPath.size()-1);if(lastNode==target){result.add(currentPath);}else{// 为了保持输出顺序与递归DFS一致,需要逆序添加邻居for(inti=graph[lastNode].length-1;i>=0;i--){intneighbor=graph[lastNode][i];List<Integer>newPath=newArrayList<>(currentPath);newPath.add(neighbor);stack.push(newPath);}}}returnresult;}}

算法分析

  • 时间复杂度:O(2^N × N)

    • 在最坏情况下(完全图),从0到n-1的路径数可能是指数级的
    • 每条路径的长度最多为N,复制路径需要O(N)时间
  • 空间复杂度

    • DFS:O(N) - 递归栈深度最多为N,不包括结果存储
    • BFS:O(2^N × N) - 队列中可能存储所有路径
    • 结果存储:O(2^N × N) - 所有路径的总空间

算法过程

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

DFS回溯

开始: node=0, path=[] ├─ path=[0] │ ├─ neighbor=1: node=1, path=[0,1] │ │ └─ neighbor=3: node=3, path=[0,1,3] → 到达目标,保存[0,1,3] │ │ path回溯为[0,1],然后回溯为[0] │ └─ neighbor=2: node=2, path=[0,2] │ └─ neighbor=3: node=3, path=[0,2,3] → 到达目标,保存[0,2,3] └─ 结束

BFS

  • 初始队列:[[0]]
  • 扩展0:[[0,1], [0,2]]
  • 扩展[0,1]:[[0,2], [0,1,3]] → 保存[0,1,3]
  • 扩展[0,2]:[[0,1,3], [0,2,3]] → 保存[0,2,3]
  • 结果:[[0,1,3], [0,2,3]]

测试用例

publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例int[][]graph1={{1,2},{3},{3},{}};System.out.println("Test 1: "+solution.allPathsSourceTarget(graph1));// [[0,1,3],[0,2,3]]// 测试用例2:直接连接int[][]graph2={{1},{}};System.out.println("Test 2: "+solution.allPathsSourceTarget(graph2));// [[0,1]]// 测试用例3:单节点int[][]graph3={{}};System.out.println("Test 3: "+solution.allPathsSourceTarget(graph3));// [[0]]// 测试用例4:多层图int[][]graph4={{1,2},{3,4},{3,4},{5},{5},{}};System.out.println("Test 4: "+solution.allPathsSourceTarget(graph4));// [[0,1,3,5],[0,1,4,5],[0,2,3,5],[0,2,4,5]]// 测试用例5:线性图int[][]graph5={{1},{2},{3},{4},{5},{}};System.out.println("Test 5: "+solution.allPathsSourceTarget(graph5));// [[0,1,2,3,4,5]]// 测试用例6:星型图int[][]graph6={{1,2,3,4},{5},{5},{5},{5},{}};System.out.println("Test 6: "+solution.allPathsSourceTarget(graph6));// [[0,1,5],[0,2,5],[0,3,5],[0,4,5]]// 测试用例7:复杂DAGint[][]graph7={{1,2},{3},{3,4},{5},{5},{}};System.out.println("Test 7: "+solution.allPathsSourceTarget(graph7));// [[0,1,3,5],[0,2,3,5],[0,2,4,5]]// 测试用例8:两个节点无连接int[][]graph8={{},{}};System.out.println("Test 8: "+solution.allPathsSourceTarget(graph8));// 测试用例9:起点等于终点的情况int[][]graph9={{}};// n=1, 起点0,终点0System.out.println("Test 9: "+solution.allPathsSourceTarget(graph9));// [[0]]// 测试用例10:较大的图int[][]graph10={{1,2,3},{4,5},{4,5},{4,5},{6},{6},{7},{}};List<List<Integer>>result10=solution.allPathsSourceTarget(graph10);System.out.println("Test 10: "+result10.size());}

关键点

  1. 回溯

    • 递归返回后移除当前节点
    • 确保不同路径之间不会相互影响
  2. 路径复制

    • 保存路径时必须创建副本(new ArrayList<>(path)
    • 否则所有保存的路径都会指向同一个List对象
  3. DAG

    • 保证是有向无环图,所以不需要visited数组
    • 如果有环,需要visited数组避免无限递归
  4. 边界情况

    • 单节点图(起点等于终点)
    • 直接连接的两个节点
    • 复杂的多分支图
  5. 顺序

    • DFS会按邻接表顺序输出路径
    • BFS会按路径长度顺序输出路径

常见问题

  1. 为什么不需要visited数组?

    • 有向无环图(DAG)
    • 不可能回到已经访问过的节点
  2. 什么时候需要创建路径副本?

    • 每次找到完整路径并要保存到结果中时
    • 因为path对象会在回溯过程中被修改
  3. BFS和DFS的输出顺序有什么区别?

    • DFS:按深度优先的顺序,通常与邻接表顺序一致
    • BFS:按路径长度顺序,先输出较短路径
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 12:09:25

20、Windows 8 网络文件与文件夹共享全攻略

Windows 8 网络文件与文件夹共享全攻略 在 Windows 8 系统中,微软致力于简化网络共享体验。像 HomeGroup 这样的功能得到了进一步改进,网络共享设置和向导也被简化,操作步骤更少。下面我们来详细了解网络共享的相关内容。 1. 了解默认网络共享设置 Windows 8 的网络共享设…

作者头像 李华
网站建设 2026/5/1 17:35:59

22、保障 Windows 8 安全与稳定的实用指南

保障 Windows 8 安全与稳定的实用指南 在数字化时代,计算机安全至关重要。对于 Windows 8 用户而言,了解并掌握系统自带的安全工具和策略,是保障系统安全稳定运行的关键。下面将为大家详细介绍 Windows 8 中一些重要的安全功能及使用方法。 1. Windows 防火墙的操作 Wind…

作者头像 李华
网站建设 2026/5/8 9:25:15

HASH表

HASH函数构造构造函数的常用方法&#xff08;下面为了叙述简洁&#xff0c;设 h(k) 表示关键字为 k 的元素所对应的函数值&#xff09;&#xff1a;为简单起见&#xff0c;假定关键码是定义在自然数集合上&#xff0c;常见的哈希函数构造方法有&#xff1a;1、直接定址法以关键…

作者头像 李华
网站建设 2026/5/6 21:03:18

关于 Bigemap Pro,这5个问题问得最多!答案都在这里

作为一款强大的地理数据处理与应用软件&#xff0c;Bigemap Pro 以其出色的易用性和兼容性&#xff0c;成为众多用户提升效率的关键工具。以下是我们汇总的 5 个高频问题及解答&#xff0c;助您快速了解其核心优势。疑问一&#xff1a;“能导入 AutoCAD / ArcGIS 吗&#xff1f…

作者头像 李华
网站建设 2026/5/7 8:04:53

Langchain-Chatchat如何实现知识库操作灰度验证?

Langchain-Chatchat如何实现知识库操作灰度验证&#xff1f; 在企业智能问答系统日益普及的今天&#xff0c;一个看似简单的问题背后往往隐藏着复杂的工程挑战&#xff1a;当你的知识库需要更新时&#xff0c;如何确保这次变更不会让AI“突然变傻”&#xff0c;甚至给出错误的关…

作者头像 李华
网站建设 2026/5/4 0:31:29

Langchain-Chatchat企业部署成本分析:自建vs.云服务哪个更划算?

Langchain-Chatchat企业部署成本分析&#xff1a;自建vs.云服务哪个更划算&#xff1f; 在当今企业智能化转型的浪潮中&#xff0c;如何高效管理和利用内部知识资产&#xff0c;已成为提升组织效率的核心命题。尤其在金融、医疗、法律等对数据安全要求极高的行业&#xff0c;一…

作者头像 李华