LinkedList的O(1)的add(int,E)复杂性如何?

2022-09-01 12:06:38

标签维基摘录:

链表是一种数据结构,其中元素包含对下一个(以及可选的上一个)元素的引用。链表提供 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;
}

即使使用半尺寸优化,这里的循环(一个或另一个)在我看来也是一个死的赠品,即这种方法(因此)在O(n)时间的最小最坏情况下运行,当然不是恒定时间。foradd(int, E)

我错过了什么?我是否误解了 big-O 符号?


答案 1

这是因为您正在阅读的文章将“获取该索引”视为单独的操作。本文假设您已经处于要执行add(int,E)的索引处。

总结:

插入或删除操作 = O(1)

查找第 n 个索引处的节点 = O(n)


答案 2

好吧,它们确实支持在任意位置的常量时间插入 - 但前提是您碰巧有一个指向列表条目的指针,在该列表条目之后或要插入某些内容的前面。当然,如果您只有索引,这将不起作用,但这不是您通常在优化代码中执行的操作。

在Java中,您也可以这样做,但只能使用列表迭代器

与数组列表相比,链接列表的这种属性是它们的最大优势 - 例如,如果要从聊天室的用户列表中删除用户,则可以在用户的用户列表中存储指向用户位置的指针,以便当他想要离开房间时,可以将其作为操作实现。O(1)