问题描述
据我所知,有以下实现:
- ArrayList
- LinkedList
- 向量
- 堆栈
(基于http://tutorials.jenkov.com/java-collections/list.html,如果有错,请正确)
ArrayList是一个动态数组实现,因此,与数组一样,get为O(1),LinkedList的Head为O(1),Vector和Stack基于ArrayList,因此为O(1)。>
因此,在每种情况下,如果O(1)都是List的,则在任何内置的get(0)上执行(因为您可以自己创建O(n!)的get(0)TS,以实现特定目的) ?
解决方法
java.util.List上的get(0)总是O(1)吗?
让我们假设有一个参数N
代表列表的长度 1 。
对于您提到的List
的4种实现,get(0)
的确是O(1)
的操作:
-
ArrayList
,Vector
和Stack
都使用数组下标实现get(i)
,这是一个O(1)
操作。 -
LinkedList.get(i)
涉及i
的{{1}}链接遍历。但是,如果O(i)
是一个常数,则可以简化为i
。
不过,O(1)
还有其他“内置”实现。的确,如果包括各种非公开的实现,例如有实现子列表,不可修改的列表等的List
类,那么它们的数量就很多。从这4个概括为“所有人”听起来不是 2 。
但是对于List
的所有可能实现,get(0)
不会是O(1)
。
-
考虑一个简单的链表,其中的元素以相反的顺序链接。由于
List
需要遍历列表的末尾,因此get(0)
链接遍历为:N
。 -
考虑一个第一次从数据库查询结果集中的行中完全填充了 的列表。第一个
O(N)
调用将至少是get
,因为您要获取O(N)
行。 (如果数据库查询不是N
,可能比O(N)
还要糟糕。)因此,对 }或更糟。
实际上,有些匠心独具的人可以发明一种自定义列表,其中O(N)
具有您想提出的任何Big-O复杂性。
1-我在这里故意含糊。一方面,我们需要标识一个变量get
来表示“问题”的大小,以便进行复杂性分析。 (列表的长度是显而易见的选择。)另一方面,当您考虑实现接口的所有可能方式时,O(N)
的长度是一个令人惊讶的“累赘”概念。
2-我假设您是在问这个问题,因为您想编写一些依赖于get(0)
是N
的库代码。由于您无法阻止某人通过非内置列表实现使用您的库,因此即使我们可以检查所有可能的情况(过去,当前或将来),您对“假设它是内置的”约束也无济于事。未来)为您内置的List
实现。
忽略自定义实现,仅查看内置实现(如问题末尾所建议的那样),无论如何,您仍然不能说get(0)
将是 O(1)列表大小。
例如,在基于get(0)
的子列表上调用LinkedList
将是 O(n):
List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5,6,7,8,9));
List<Integer> subList = list.subList(4,8);
Integer num = subList.get(0); // <===== O(n),not O(1)
在该代码中,subList.get(0)
内部调用list.get(4)
,时间复杂度为O(n)。
是的,对于您提到的List
的所有实现,get(0)
是O(1)。