我正试图解决这个问题:https://www.hackerrank.com/challenges/journey-to-the-moon I.e。找到图的连通分量的问题。我所拥有的是一个顶点列表(从0到N-1),标准输入中的每一行给出了一对由边连接的顶点(即如果我有1, 3
)则意味着顶点1和顶点3在一个连接的组件中。我的问题是存储inpit的最佳方法是什么,即如何表示我的图表?我的想法是使用Arraylist的ArrayList - 数组列表中的每个位置都存储另一个adgecent顶点的arraylist。这是代码:
public static List<ArrayList<Integer>> graph;
然后在main()
方法中:
graph = new ArrayList<ArrayList<Integer>>(N);
for (int j = 0; j < N; j++) {
graph.add(new ArrayList<Integer>());
}
//then for each line in the standard input I fill the corresponding values in the array:
for (int j = 0; j < I; j++) {
String[] line2 = br.readLine().split(" ");
int a = Integer.parseInt(line2[0]);
int b = Integer.parseInt(line2[1]);
graph.get(a-1).add(b);
graph.get(b-1).add(a);
}
我很清楚,为了解决这个问题,我必须将顶点a
放在位置b-1
,然后将顶点b
放在位置a-1
,这样就不会改变。但我要找的是表示图表的更好方法吗?
答案 0 :(得分:0)
使用Java的集合(例如,ArrayList)会增加大量内存开销。除了存储int所需的4个字节外,每个Integer对象至少需要12个字节。
只需使用一个巨大的单个int数组(让它称之为edgeArray),它代表邻接矩阵。当单元格对应边缘时输入1。例如,如果在输入上看到节点k和m,则单元(k,m)将具有1,否则为0.在行主要顺序中,它将是索引k * N + m。即edgeArray [k * N + m] = 1.您可以选择column major order或行主要顺序。但是你的int数组将非常稀疏。实现稀疏数组非常简单。只需按排序顺序排列非零索引数组。它应该按排序顺序,以便您可以二进制搜索。元素的数量将按边数的顺序排列。
当然,当您构建邻接矩阵时,您不会知道有多少边。所以你不能分配数组。只需使用哈希集。不要使用HashSet,这是非常低效的。从fastutils看IntOpenHashSet。如果不允许使用库,请实现类似的库。
我们假设您将使用的openHashMap变量称为adjacencyMatrix。所以,如果你看到,3和2,总共有10 ^ 6个节点(N = 10 ^ 6)。然后你就会做
adjacencyMatirx.add(3 * 10000000 + 2);
处理完所有输入后,您可以在上面进行稀疏邻接矩阵实现:
final int[] edgeArray = adjacencyMatrix.toIntArray(new int[adjacencyMatrix.size()]);
IntArrays.sort(edgeArray)
给定节点,找到所有相邻节点: 因此,如果您需要连接到节点p的所有节点,您将二进制搜索下一个大于或等于p * N的值(O(log(边数)))。然后你将遍历数组,直到你达到一个大于或等于(p + 1)* N的值。你遇到的所有值都是连接到p的节点。
将其与您在问题中提到的方法进行比较: 它使用O(N * b)空间复杂度,其中N(节点数)和b是分支因子。它的下限受边缘数量的限制。
对于我提到的方法,空间复杂度只是O(E)。实际上,它只是整数个数加上int数组的标题。
答案 1 :(得分:0)
我使用了var graph = new Dictionary<long, List<long>>();
请参阅此处了解c#中的完整解决方案 - https://gist.github.com/newton3/a4a7b4e6249d708622c1bd5ea6e4a338
PS - 2年,但万一有人偶然发现了这一点。