节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。
示例1:
示例2:
提示:
节点数量n在[0, 1e5]范围内。
节点编号大于等于 0 小于 n。
图中可能存在自环和平行边。
做这道题之前,需要你对DFS(深度优先算法),BFS(广度优先算法),邻接表(一种存储结构),有向图(图的一种,是一种数据结构)有着一些基本的了解。否则你光干瞪着眼看题,是看不出什么结果的。
首先从头来解释一下这道题,首先题目给的graph是一个二维数组,其中的每一个元素都是一个存储着两个元素的vector。vector中的每一个不重复的数字,都代表着一个节点。每一个vector中都有两个元素,将这两个元素想象成两个节点,那就是第一个节点指向第二个节点。
将graph中的每一个节点的指向都画出来的话,你就可以得到一张有向图。
首先,我们需要题目给的有向图去存储到邻接表之中去。那么存储图的邻接表的代码长这样:
vector> vec(n);
其中n就是节点的个数啦。可以想象,邻接表就是将图中的每一个节点都当做一个链表,节点本身就是链表的头节点。后面连着几个节点,我们就将这些节点添加到头节点对应的vector中去。
那这道题解题的第一步就是,将图按照邻接表的形式存储在vector中。
第二步就是深度优先遍历,深度优先遍历的本质上是递归。因为我们都知道了,二维vector中的每一个vector都代表着一条路线。我们只需要将每条路线都一一走到头,就能判断出来,有没有题目中给出的start -> target的这条路线了。
具体的代码如下:
class Solution {
public:// 邻接表+DFSbool findWhetherExistsPath(int n, vector>& graph, int start, int target) {// 初始化邻接表为容量为n的二维向量, 存储有向边vector> vec(n); // 外面的这个vector有n个元素,都是vector// 将图中的有向边存入邻接表Vecfor (const auto& g : graph) { //g是一个vector,里面存放了两个元素。vec[g[0]].push_back(g[1]);//下标为g[0]的vector中添加了一个数字,这个数字是g[1]}// 调用dfs函数,执行深度优先搜索return dfs(vec, start, target);}bool dfs( vector>& vec, int start, int target) {// 如果起点和终点相同,则存在连通路径,返回trueif (start == target) {return true;}// 依次遍历从起点可直接到达的节点v,递归地调用函数,并以v为新起点进行下一轮搜索for (const auto& v : vec[start]) { //这个vec[start]本身代指的是一个一维数组。if(dfs( vec, v, target)) {return true;}}// 若所有路径都走不到终点,则不存在连通路径,返回falsereturn false;}
};
时间复杂度:
对于邻接表的创建,需要遍历一次整个图中所有的边,其时间复杂度为 O(E),其中 E 表示边数。
对于 DFS 遍历,每一个点最多只会被访问一次,在最坏情况下,需要访问图中所有的点,因此其时间复杂度为 O(n),其中 n 表示点数。
综上所述,该算法的时间复杂度为 O(E + n)。
空间复杂度:
邻接表使用了一个二维向量来存储边信息,其大小为 O(E),其中 E 表示边数。由于 E 不超过 n^2 的级别,因此该向量的空间需求不会超过 O(n^2)。
在 DFS 的递归调用过程中,根据深度可能会生成 O(n) 个递归层级。而每一层递归函数在栈中占用的空间是常数级别的 O(1),因此 DFS 所需要的空间是 O(n) 级别的。
综上所述,该算法的空间复杂度为 O(n^2+n),即 O(n^2) 级别。
因此,总的来说,该算法的时间复杂度为 O(E+n),空间复杂度为 O(n^2)。
下一篇:HBase详解