从链接列表标签Wiki摘录:
链表是一种数据结构,其中的元素包含对下一个(以及可选的上一个)元素的引用。链接列表可 在任何位置 提供 O(1)插入和删除
,O(1)列表串联,以及在前(和可选)后位置的O(1)访问以及对下一个元素的O(1)访问。随机访问具有O(N)的复杂度,通常没有实现。
(强调我的)
令我惊讶的是,该列表如何以比简单 读取 该索引低的复杂度 插入 一个随机索引? __
因此,我查看了的源代码java.util.LinkedList
。该add(int, E)
方法是:
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
}
该addBefore(E, Entry<E>
方法只是指针的重新分配,但是还有entry(int)
方法:
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
即使进行了一半大小的优化,for
这里的循环(一个或另一个)对我来说似乎都是死胡同,这种方法(因此add(int, E)
)在O(n)时间的最小最坏情况下运行,并且肯定不是恒定的时间。
我想念什么?我是否误解了big-O表示法?
好的,它们确实支持在任意位置进行固定时间插入-但 前提是
您碰巧有一个指向列表条目的指针,您想在列表条目之后或之前插入某个条目。当然,如果只有索引,这将无法正常工作,但这不是您通常在html" target="_blank">优化代码中执行的操作。
在Java中,您也可以这样做,但只能使用列表迭代器。
相较于数组列表,链表的此属性是它们的最大优点–例如,如果要从聊天室的用户列表中删除用户,则可以将指向用户位置的指针存储在用户的用户列表中,以便,当他想离开房间时,可以将其实现为一项O(1)
操作。
问题内容: 为什么ArrayList的add()和add(int index,E)复杂度要按固定时间摊销? 为什么不对单个add()操作使用O(1),对单个add(int索引,E)操作使用O(n),而对于使用任一(任何)添加方法添加n个元素(n个添加操作)的O(n)呢?假设我们很少使用add(int index,E)添加到数组末尾? 数组(和ArrayList)的操作复杂度已经不是n个元素了: a
问题内容: 我打算对StringBuilders中的最后一个字符进行很多删除。使用的解决方案对我来说很好。但是,由于这些删除将处于循环中,因此我需要知道其复杂性。 据我了解,该操作只是减少了StringBuilder对象的一些私有属性,并且不对字符本身执行任何复制/克隆/复制操作,因此它的时间为O(1),应该可以快速运行。 我对吗? 问题答案: 从文档中: 设置字符序列的长度。序列更改为新的字符序
下面是链接:array reversed() 在讨论部分的最后,它说复杂性O(1),我相信这是关于时间复杂性的,如何反转一个数组需要O(1)时间?
在最近的一次测试中,我们得到了一个函数来计算未排序的ArrayList中出现了多少个double(不是原语double,而是一个项目出现了两次)。 我正确地确定了Big O复杂度为O(N^2),但由于我错误地确定了全部复杂度,因此只获得了部分学分。函数如下: 在他刚刚发布的考试解决方案中,他给出了这样的解释: 输入集合中有N个项,该方法通过一个缩减步骤反复调用自己,该步骤生成一个新索引N次,直到达
我不明白堆排序的空间复杂度是怎样的O(1)?虽然快速排序不使用任何额外的数组(即就地),但在最坏情况下其空间复杂度为O(n),在最佳情况下为O(lg n),因为在递归调用的后端使用堆栈。我说得对吗? 堆排序也是如此。虽然,它是就地的,但是由于Build-Heap函数调用Max-Heapify函数,所以它的空间复杂度应该等于Max-Heapify,即O(lg n)。不是吗?此外,稍后在根节点调用Ma
我对在LinkedList开头插入元素的时间复杂性感到好奇。我知道LinkedList本身会将现有元素向右移动一个索引,但要做到这一点,它会进行与列表中现有元素一样多的迭代吗? 另外,最好的方式是在开头插入offerFirst吗?