【问题标题】:ArrayList Get Operation O(1). How?ArrayList 获取操作 O(1)。如何?
【发布时间】:2013-09-25 17:46:54
【问题描述】:

stack overflow answers 之一中,关于为什么 ArrayList.get 是 O(1) 而不是 O(n)

响应者说 ArrayList 是由一个数组和一个

      RangeCheck(index); 

是什么决定了恒定的时间复杂度而不是线性的!我正试图解决这个问题,任何数组都不必至少部分搜索数组中可能 n 个字段的百分比,从而以某种方式构成 O(n) 搜索(如果要寻找的元素在数组的 n-1 位置 - 这不是 O(n) 搜索吗?它仍然是我认为是 O(n) 复杂度的一级 for 循环?

【问题讨论】:

  • ArrayList.get(int) 不搜索。它直接返回由提供的索引寻址的元素...与数组完全一样-因此得名。 (另外,我认为这是关于 Java 的)。不过,对于 LinkedList,这将成立!
  • 该链接应该转到页面吗?你在这里说的是什么语言?
  • 是的,这是关于 Java 的!
  • @ppeterka66 您应该将其添加为答案。伍布尔:Java。 ArrayList.get
  • 答案很简单,ArrayList 的内部表示是一个数组。所以你可以通过索引直接访问!

标签: java big-o


【解决方案1】:

数组在内存中按顺序排列。这意味着,如果它是一个整数数组,每个使用 4 个字节,并且从内存地址 1000 开始,则下一个元素将位于 1004,下一个元素位于 1008,依此类推。因此,如果我想要数组中位置 20 的元素,get() 中的代码将不得不计算:

1000 + 20 * 4 = 1080

获得元素的确切内存地址。好吧,RAM 存储器的名称为 Random 存取存储器,因为它们的构建方式使得它们具有硬件多路复用器的层次结构,允许它们在恒定时间内访问任何存储的内存单元(字节?),给了地址。

因此,两个简单的算术运算和一个对 RAM 的访问被称为 O(1)。

【讨论】:

  • +1,真正解决了为什么这是 O(1) 的根源,而不是仅仅假设数组是 O(1)。我要补充一点,“数组在内存中是按顺序排列的”,这就是数组必须具有固定大小的原因。你永远无法确定那个数组后面是什么内存。
  • 顺便说一句,我一直想知道:内存访问不是 O(内存地址中的位)吗?也就是说,32位系统上的内存访问应该比64位系统快?当然,电路必须为每个位做出至少 1 个决定,对吗?此外,内存实际上是字节可寻址的。
  • 嗯,只要(内存地址中的位)保持不变,O(内存地址中的位)仍然是,根据 big-o 数学定义,O(1)...但是硬件确实更复杂,这一切都是简化,涉及的层和算法更多,包括L1缓存、L2缓存、MMU和虚拟地址表。
  • 但内存地址中的位不是恒定的。我的系统可能比你的多。对于同一个系统,它始终是恒定的,但通常不是恒定的。我知道虚拟寻址、寄存器和缓存等,但我说的是当内存访问必须实际发生时,在 64 位系统上是否需要更长的时间(所有其他变量都相同)?
  • 如果内存是一个大链表,我必须遍历整个内存空间才能找到我的一块内存,你会说:它是 O(n),其中 n 是内存量.你不会说:它是 O(1),因为系统总是有相同数量的内存。
【解决方案2】:

按照建议作为答案发布:

ArrayList.get(int) 不搜索。它直接返回由提供的索引寻址的元素...与数组完全一样 - 因此得名。

ArrayList.get(int) 来源:

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

其中 rangeCheck(int) 是:

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

而 elementData() 是:

E elementData(int index) {
    return (E) elementData[index];
}

Linked List 但是会有一个O(n) get:它必须跳到下一个元素,直到达到所需的索引...

public E get(int index) {
    return entry(index).element;
}

entry(int) 在哪里(这就是 O(n)):

private Entry<E> More ...entry(int index) {
    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) )

【讨论】:

  • 谢谢!内存关联和代码分解给出了 360 度全景图!
【解决方案3】:

访问数组中的特定索引是一个常数时间操作,因为它涉及从数组对象中获取基内存地址,并访问基地址 + 索引乘以元素类型大小的内存。由于中间的所有元素都被跳过,因此访问时间受一个常数的约束。

ArrayList.get(int) 方法是对直接数组访问的精简包装,因此它也受一个常量的限制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-20
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 2018-08-12
    相关资源
    最近更新 更多