for循环语句java图形
在我第一次接受全栈软件工程职位的现场采访时,我被要求编写一个函数,该函数可以检测有向图中是否存在循环。
读者:我没有成功编写该函数。
坦白说,我有一个什么样的图形,甚至是朦胧的,在最佳的想法。 在我一生的大部分时间里,我都以为这张图是这样的:
原来,图实际上是这样的:
我知道,我认为第一个看起来也更有趣。 但是图实际上是一个非常酷的数据结构。 它们在计算机科学中无处不在,用于推荐引擎和Google Maps ,当然还有GraphQL 。 问题是,没有多少文章详细介绍Javascript中图的实现/技巧。 所以我决定写一个。
那图到底是什么?
关于图的最基本知识是它们由顶点和边组成。 顶点是图的东西 :一个整数,对象,一个网站等边连接一对顶点。
图形可以是:
- 加权或未加权-这是指图形的边缘是否用值标记。 如果是,则对其进行加权。
- 有向还是无向-这是指边缘的行为像桥(无向)还是保龄球道(有向)。
- 循环或非循环-这是指图形中是否存在循环。
表示图形的主要方法有两种:使用邻接表 ,它是与每个节点相关联的数组(或对象,如果需要的话)的集合:
{
a: [b,c,d],
b: [c,f],
d: [e],
e: [a,f],
f: [a, c, d, e]
}
或带有邻接矩阵,这是一个二维数组,其中顶点之间的边用1表示:
[ [0, 1, 0, 0, 0, 0, 1, 0, 1, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 0, 1, 0, 0, 0, 1],
[0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0] ]
每种表示形式都有其优缺点 。 一般而言,具有大量边的图的表现比矩阵更好,而具有较少边的图的表现比列表更好。 邻接列表往往更常见,而今天我们将使用它。
制作图表
因此,让我们开始吧! 呜。 我们要做的第一件事是…制作一张图表。
Graph构造函数内部的逻辑很简单:只是实例化为对象文字的邻接列表。 接下来的两种方法并不复杂:添加顶点的方法将每个顶点初始化为一个空数组,添加边缘的方法将一个顶点推入另一个顶点的数组。 像这样
制作一个看起来像这样的图:
或换一种说法:
现在我们有了一个图! 好极了。 不仅仅是任何图:未加权的有向无环图。
遍历图
现在我们有了一个图,我们将需要找出一种访问不同顶点的方法—毕竟,我们的最终目标是检测该图是否为周期性的,这意味着沿着顶点在顶点之间遍历。图的边缘。
有两种遍历图形的方式: 广度优先遍历,即先访问顶点的每个子级,然后再访问该子级中的每个子级;以及深度优先遍历,即遵循由边连接的一系列顶点,直到我们到达顶点为止在继续到原始顶点的下一个子节点之前,不能再进行任何操作。 我们在有向图中检测循环的算法将使用深度优先遍历的修改版本,因此让我们快速看一下该遍历方法的外观。
这里有两个函数:dfs函数和_dfsUtil函数。
dfs函数仅做三件事:
- 创建一个名为“ nodes”的数组; 每个元素都是我们图中的一个顶点
- 创建一个称为“ visited”的对象文字
- 在图形的每个顶点上调用我们的效用函数。
与二叉树(例如)相反,在执行图的深度优先遍历时,要记住的重要一点是,我们需要跟踪已经访问过的节点,而不是第二次访问它们,即使另一个顶点在该节点上有一条边。 例如,如果我们不跟踪所访问的顶点,那么从起点“ A”遍历图形将导致我们四次访问“ B”,而不仅仅是一次! 🙅🙅🙅
A -> B
A -> D -> B
A -> D -> E -> B
A -> C -> E -> B
这就是为什么我们将实用程序函数作为参数传递访问数组的原因。 效用函数是我们访问顶点的地方,将其标记为已访问,获取其边缘,检查给定的边是否已被访问,如果尚未访问,则以该顶点为新起点递归调用我们的效用函数,并更新的“访问”数组。
但是...周期检测?
对! 检测周期。 我们如何修改深度优先搜索功能来确定图是循环的还是非循环的?
和以前一样,我们将为图形中的每个顶点调用循环检测实用程序函数。 如果效用函数从不返回true,我们只能确定图是非循环的。 但是,我们还将创建一个名为“ recStack”的对象文字。
我们在这里做几件事:
- 在第45行,我们正在检查以确保我们尚未访问此节点。 如果有,则无需再次检查。
- 在第46和47行上,我们在访问堆栈和recStack中将顶点设为true。 在这个新的实用函数中,recStack代表“递归堆栈”,它一直在跟踪后端,我们访问的顶点以使我们到达当前的顶点。
- 和以前一样,我们将获取当前顶点的邻居并遍历它们。 但是,这一次,我们不仅要检查是否已经访问过它们,还要检查是否在recStack中存在特定的边缘,这意味着我们已经访问过它。 这是我们发现周期的指标。
- 如果顶点不在我们的recStack中,那么我们会将其从递归堆栈中弹出(第59行),这样,当我们遍历图的另一条路径时,我们就不会得到假阳性。
嘿,请记住:这是我们检测图形中是否存在循环的功能。
我看过的大多数计算机科学入门资源中都没有涉及图形,这实在令人遗憾。 他们真的很酷,遍及整个学科,而且在入门级软件开发职位的面试中很普遍(或者至少在我的经验中如此)。 因此,对我的(有抱负的)初级开发人员来说:在进行下一次面试之前,请先学习一些图表。 你永远不会知道。
这是副本: https : //repl.it/@samisthesam/Cycle-detection-in-a-directed-graph
感谢 Sulamita Morales 的咨询和道义支持
for循环语句java图形