考虑一个情况,我们可以在List<List<Integer>>
的帮助下定义一个图,其中索引 0 处的列表将是顶点 0 的邻接列表,依此类推 1,2,3 及更远。
现在,如果我声明这样的邻接列表如下,
// initialize adjacency list
List<List<Integer>> adjList = new ArrayList<List<Integer>>(n);
在这种情况下,为什么我需要初始化每个列表,如下所示,
// initialize vertices
for (int i = 0; i < n; i++)
adjList.add(i, new ArrayList<Integer>());
此循环仅在每个索引 i 处添加一个新列表,其中 n = 图中的顶点数。
初始声明是否意味着主列表将在索引 0 处有一个列表,然后在索引 1 处再次具有另一个列表,依此类推?
我的意思是,如果我在列表数组的帮助下声明一个图形,我可以理解这个循环的使用,如下所示,
ArrayList[] graph = new ArrayList[n];
那么在这种情况下,是的,我肯定需要一个循环来初始化每个顶点的邻接列表,但为什么我们也需要在这种初始情况下初始化呢?
如果我按如下方式声明数组怎么办?这会对循环产生任何影响吗?
1. List<List<Integer>> adjList = new ArrayList<>(n);
2. List<List<Integer>> adjList = new ArrayList<List<List<Integer>>(n);
请帮助我,我为了理解这一点而犯的概念错误是什么?
当你有:
List<List<Integer>> adjList = new ArrayList<List<Integer>>(n);
您只实例化了初始容量为n
的ArrayList
,但列表为空(即adjList.size() == 0
(。ArrayList
的容量是其内部阵列的大小。当你向ArrayList
添加元素时,如果数组不够大,无法容纳另一个元素,则必须创建一个新的、更大的数组,并且必须复制旧数组的内容。这可能很昂贵;如果您知道所需的容量,则可以指定初始容量,以避免"调整"内部阵列的大小。
如果List
必须有一定数量的元素开始,你必须首先添加它们,这就是循环的作用:
for (int i = 0; i < n; i++)
adjList.add(i, new ArrayList<Integer>());
以下是List#add(int,E)
的文档:
在此列表中的指定位置插入指定的元素(可选操作(。将当前位于该位置的元素(如果有(和任何后续元素向右移动(将一个元素添加到其索引中(。
由于您从一个空List
开始,然后从0
迭代到n
因此您只需在每次迭代List
末尾添加一个元素。这相当于每次迭代都调用List#add(E)
。循环完成后,您将拥有一个大小等于n
的非空List
。
如果我按如下方式声明数组怎么办?这会对循环产生任何影响吗?
1. List<List<Integer>> adjList = new ArrayList<>(n); 2. List<List<Integer>> adjList = new ArrayList<List<List<Integer>>(n);
- 这只是使用钻石运算符。菱形运算符在使用泛型类型时删除样板。
- 这不会编译;左侧是列表列表,但右侧是列表列表。