Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 邻接矩阵的图实现与初始化_Java_Algorithm_Optimization_Data Structures_Graph - Fatal编程技术网

Java 邻接矩阵的图实现与初始化

Java 邻接矩阵的图实现与初始化,java,algorithm,optimization,data-structures,graph,Java,Algorithm,Optimization,Data Structures,Graph,图通常用邻接矩阵表示。各种来源表明,可以避免初始化成本为| V^2 |(V是顶点数),但我可能还没有弄清楚如何进行初始化 在Java中,只需声明矩阵,例如boolean adj[][],运行时就会自动用false初始化数组,这将以O(V^2)为代价(数组的维度)。 我误解了吗?是否有可能避免邻接矩阵初始化的二次成本,或者这仅仅是依赖于实现语言的理论问题?这可能通过使用邻接矩阵的稀疏矩阵表示,其中只有“一”的位置是分配的,而不是矩阵的每个元素(可能包含大量的零)。您可能会发现,矩阵值的默认初始化实

图通常用邻接矩阵表示。各种来源表明,可以避免初始化成本为| V^2 |(V是顶点数),但我可能还没有弄清楚如何进行初始化

在Java中,只需声明矩阵,例如
boolean adj[][]
,运行时就会自动用
false
初始化数组,这将以O(V^2)为代价(数组的维度)。

我误解了吗?是否有可能避免邻接矩阵初始化的二次成本,或者这仅仅是依赖于实现语言的理论问题?

这可能通过使用邻接矩阵的稀疏矩阵表示,其中只有“一”的位置是分配的,而不是矩阵的每个元素(可能包含大量的零)。您可能会发现,矩阵值的默认初始化实际上是一项功能。如果没有默认的初始化,您是否仍然需要自己初始化每个字段,以便知道它的值是什么

邻接矩阵有一个缺点:它们在内存效率方面很差(它们需要O(n2)个内存单元),并且正如您所说,它们的初始化速度较慢。然而,初始化从未被认为是最大的问题。相信我,内存分配要慢得多,所需的内存比初始化时间要有限得多


在许多情况下,人们更喜欢使用邻接列表,而不是矩阵。这样的列表需要
O(m)
内存,其中
m
是图形中的边数。这是非常有效的,特别是对于稀疏图。此图表示法比邻接矩阵更糟糕的唯一操作是查询顶点i和j之间是否有边。矩阵在
O(1)
时间内回答,列表肯定会慢很多


然而,许多典型的图算法(如、和)只需要迭代给定顶点的所有邻域。如果您使用邻接列表而不是矩阵,所有这些算法都将受益匪浅。

我将详细介绍A_A的答案。他建议使用稀疏矩阵,这基本上意味着您可以重新维护邻接列表

使用矩阵有两个原因——如果你根本不关心性能,喜欢它提供的简单代码,或者如果你确实关心性能,但矩阵将相对完整(为了这篇文章的缘故,假设至少有20%的矩阵是完整的)


你显然很在乎表现。如果你的矩阵是相对空的,它的初始化开销是有意义的,你最好使用邻接列表。如果它将非常满,初始化将变得微不足道-您需要在矩阵中填充正确的单元格(这将花费比初始化更多的时间),并且您需要处理它们(同样,这将花费比初始化更多的时间)。

此线程中存在大量混乱和错误信息。事实上,有一种方法可以避免邻接矩阵(以及一般的任何数组)的初始化开销。但是,不可能将该方法用于Java原语,因为它们是在后台用默认值初始化的

假设您可以创建一个未自动初始化的数组
数据[0..n]
。首先,它充满了以前记忆中的垃圾。如果我们不想花费O(n)时间覆盖它,我们需要某种方法来区分我们添加的好数据和垃圾数据

“诀窍”是使用一个辅助堆栈来跟踪包含良好数据的单元格。第一次写入
数据[i]
时,我们将索引
i
添加到堆栈中。由于堆栈只会随着我们的添加而增长,因此它从不包含任何我们需要担心的垃圾

现在,每当我们访问
数据[k]
,我们都可以通过扫描堆栈中的
k
来检查它是否垃圾。但是每次读取都需要O(n)个时间,首先会破坏数组的点

为了解决这个问题,我们制作了另一个辅助数组
stack\u check[0..n]
,它也开始充满垃圾。此数组包含指向堆栈中元素的指针。现在,当我们第一次写入
data[i]
时,我们将
i
推到堆栈上,并将
stack\u check[i]
设置为指向新的堆栈元素

如果
data[k]
是好数据,则
stack\u check[k]
指向包含
k
的堆栈元素。如果
data[k]
是垃圾,那么
stack\u check[k]
的垃圾值要么指向堆栈外部,要么指向堆栈元素
k
(因为
k
从未放在堆栈上)。检查此属性只需要O(1)个时间,因此我们的阵列访问速度仍然很快


把所有这些放在一起,我们可以在O(1)时间内创建数组和助手结构,让它们充满垃圾。在每次读写时,我们都会使用助手检查给定单元格是否在O(1)时间内包含垃圾。如果我们写的是垃圾邮件,我们会更新助手结构,将单元格标记为有效数据。如果我们阅读垃圾,我们可以用任何适合给定问题的方式来处理它。例如,我们可以返回一个默认值,如0(现在你甚至不知道我们没有初始化它!),或者抛出一个异常。

邻接矩阵的稀疏表示只是一个邻接列表。那么你是说没有某种“技巧”可以在不到二次时间内初始化数组?甚至在理论上?@user384706不,没有,理论上也不可能有。然而,有些函数使用了高度优化的操作,比如c++的
memset
函数。它们只是在某些情况下的内存效率不好。我知道你想说什么,但我