# 编程学习笔记(LeetCode-797. 所有可能的路径)
2021/8/25 17:07:51
本文主要是介绍# 编程学习笔记(LeetCode-797. 所有可能的路径),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
编程学习笔记(LeetCode-797. 所有可能的路径)
<797> 所有可能的路径
- 问题重述:
给定一个有 \(n\) 节点的 有向无环图(DAG) ,现需要你找出所有从节点 \(0\) 到节点 \(n-1\) 的路径,并且输出(路径顺序任意)。
在本题中,有向无环图,用一个二维数组表示,第i
个数组中的单元都表示有向图中i
号节点所能到达的下一些节点,空就是没有下一个结点了。
例如:
- 对于二维数组
graph = [ [1,2], //0号节点出发能到的节点 [3], //1号节点出发能到的节点 [3], //2号节点出发能到的节点 [] //3号节点出发能到的节点 ]
有图:
graph LR 0 --> 1 0 --> 2 1 --> 3 2 --> 3- 对于二维数组
graph = [ [4,3,1], //0号节点出发能到的节点 [3,2,4], //1号节点出发能到的节点 [3], //2号节点出发能到的节点 [4], //3号节点出发能到的节点 [] //4号节点出发能到的节点 ]
有图:
graph TB 0 --> 4 0 --> 3 0 --> 1 1 --> 3 1 --> 2 1 --> 4 2 --> 3 3 --> 4注: 在图中,若规定了 a → b 那么你就不能 b → a。
示例1:
输入: graph = [[1,2],[3],[3],[]]
输出: [[0,1,3],[0,2,3]]
解释: 有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
示例2:
输入: graph = [[4,3,1],[3,2,4],[3],[4],[]]
输出: [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
示例 3:
输入: graph = [[1],[]]
输出: [[0,1]]
示例 4:
输入: graph = [[1,2,3],[2],[3],[]]
*输出: [[0,1,2,3],[0,2,3],[0,3]]
示例 5:
输入: graph = [[1,3],[2],[3],[]]
输出: [[0,1,2,3],[0,3]]
你可以假定:
- \(n == graph.length\)
- \(2 \leqslant n \leqslant 15\)
- \(0 \leqslant graph[i][j] < n\)
- \(graph[i][j] \neq i\)(即,不存在自环)
- \(graph[i]\) 中的所有元素 互不相同
- 保证输入为 有向无环图(DAG)
问题解答:
在一张有向图中,要考究从某个节点(0
号节点)出发,是否能到另一个节点(n-1
号节点),那么就是要尝试遍历所有路径,看是否能到达目标节点。而遍历一张图的所有路径一般的方法有:广度优先遍历(BFS) 和 深度优先遍历(DFS)。
那么对于本题,经过分析,我们易知,需要遍历所有的路径,那么对于全路径遍历的情况,无论是采用DFS或是BFS,性能相当,所以这里就采用深度优先遍历的方法。
又因为题目给定的是一个 有向且无环 的图(DAG),因此在深度遍历搜索的过程中不会反复地遍历同一个节点,因此无需判断当前的点是否遍历过。
对于 深度遍历搜索(DFS),实现它的方法就是用 栈(Stack) ,从起点出发,使用栈去记录路径上的点,当发现可以遍历到目标节点的时候,就将栈中记录的路径加入到答案集中。
- 例如之前提到的一张图:
graph = [ [4,3,1], //0号节点出发能到的节点 [3,2,4], //1号节点出发能到的节点 [3], //2号节点出发能到的节点 [4], //3号节点出发能到的节点 [] //4号节点出发能到的节点 ]
我们把它所有可能的路径都画出来,那么可以画出如下的路线图,其实他就是一颗解答树,我们就是要想办法用程序去遍历这颗解答树:
graph TD start(0) level1a([4]) level1b(3) level1c(1) level2a([4]) level2b(3) level2c(2) level2d([4]) level3a([4]) level3b(3) level4a([4]) start --> level1a start --> level1b start --> level1c level1b --> level2a level1c --> level2d level1c --> level2b level1c --> level2c level2b --> level3a level2c --> level3b level3b --> level4a正如前面所说,遍历题目所给的有向无环图,其实就是要我们采用何种方法去遍历这颗解答树,此处我们采用 深度优先(DFS) 的策略去遍历这颗解答树。
在用程序解答这棵解答树的时候,我们需要声明一个栈stack
,用来存储当前遍历路径节点,但是为了省事,这里我直接使用程序的 执行栈 ,说人话就是使用 递归函数 。那么程序如下所示(采用C++语言):
采用递归的方法,实现DFS(C++):
class Solution { public: vector<vector<int> > result; //用于存储返回结果 vector<int> cur_road; //用于记录当前路径 void dfs(vector<vector<int>>& graph, int n_1){ //判断当前路径的最后一个节点是否是目标节点 //如果是则记录并返回 //否则继续遍历 if(this->cur_road.back() == n_1){ this->result.push_back(this->cur_road); return; } //题目给定的是一个 有向无环图(DAG), //因此在深度遍历搜索的过程中不会反复地遍历同一个节点, //因此无需判断当前的点是否遍历过。 for(int var: graph[this->cur_road.back()]){ //遍历当前节点的所有子节点 //开始解答其中一个子节点 this->cur_road.push_back(var); dfs(graph, n_1); //遍历完之后要记得 pop 出去, //表示解答树中的当前节点以及它的所有后代已经遍历完了, //要开始解答兄弟节点了,当解答完所有兄弟节点,就返回到父亲节点 this->cur_road.pop_back(); } } vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) { //初始化全局变量 this->cur_road.push_back(0); //开始递归遍历 dfs(graph, graph.size()-1); //返回结果 return this->result; } };小提示: 在使用递归函数的时候要注意跳出递归的逻辑是否正确;还有就是递归函数参数列表中参数个数最好少一点;因为递归函数使用的是执行栈,执行栈对于一个程序来说还是很重要的,要避免栈溢出这种致命的错误。
时空复杂度分析:
- 时间复杂度: \(O(n \times 2^n)\)
在最坏的条件下,遍历时,每一个点都往编号比他大的点走,此时遍历的路径数为 \(O(2^n)\) ,路径长度为 \(O(n)\) ,所以时间复杂度为:\(O(n \times 2^n)\) 。
- 空间复杂度: \(O(n)\)
因为返回值不计入空间复杂度,那么此处主要是执行栈空间的开销。
这篇关于# 编程学习笔记(LeetCode-797. 所有可能的路径)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享