【问题标题】:What is the time complexity of indexing, inserting and removing from common data structures?从常见数据结构中索引、插入和删除的时间复杂度是多少?
【发布时间】:2008-09-23 18:25:01
【问题描述】:

对于最常见的数据结构(包括数组、链表、哈希表等)的操作,没有可用的大 O 表示法的总结。

【问题讨论】:

    标签: data-structures complexity-theory big-o


    【解决方案1】:

    有关此主题的信息现在可在 Wikipedia 上找到:Search data structure

    +----------------------+----------+------------+----------+--------------+
    |                      |  Insert  |   Delete   |  Search  | Space Usage  |
    +----------------------+----------+------------+----------+--------------+
    | Unsorted array       | O(1)     | O(1)       | O(n)     | O(n)         |
    | Value-indexed array  | O(1)     | O(1)       | O(1)     | O(n)         |
    | Sorted array         | O(n)     | O(n)       | O(log n) | O(n)         |
    | Unsorted linked list | O(1)*    | O(1)*      | O(n)     | O(n)         |
    | Sorted linked list   | O(n)*    | O(1)*      | O(n)     | O(n)         |
    | Balanced binary tree | O(log n) | O(log n)   | O(log n) | O(n)         |
    | Heap                 | O(log n) | O(log n)** | O(n)     | O(n)         |
    | Hash table           | O(1)     | O(1)       | O(1)     | O(n)         |
    +----------------------+----------+------------+----------+--------------+
    
     * The cost to add or delete an element into a known location in the list 
       (i.e. if you have an iterator to the location) is O(1). If you don't 
       know the location, then you need to traverse the list to the location
       of deletion/insertion, which takes O(n) time. 
    
    ** The deletion cost is O(log n) for the minimum or maximum, O(n) for an
       arbitrary element.
    

    【讨论】:

    • 数组中的删除有些混乱。有人说,找到要删除的元素需要 O(n) 时间。然后为了删除它,您必须将其右侧的所有元素向左移动一个空格。这也是 O(n),因此总复杂度是线性的。也有人说,空格不用填,最后一个元素填即可。
    • 另外,如果我们想在数组的第一个位置插入一个元素怎么办?这不会导致整个数组移动吗?那么 O(n) 不应该是数组的插入时间吗?
    • 请注意,您需要区分 未排序已排序数组。移动/填充数组的元素只是排序数组的问题,因此线性复杂度而不是未排序数组上的O(1)。关于查找要删除的元素的想法,您必须再次区分查找元素和删除元素。删除的复杂性假设您已经知道要删除的元素,这就是为什么您在已排序数组(需要移位)上使用O(n),在未排序数组上使用O(1)
    • 这就解释了。谢谢!
    【解决方案2】:

    我想我将从链表的时间复杂度开始:

    索引---->O(n)
    在末尾插入/删除---->O(1) 或 O(n)
    在中间插入/删除--->O(1) with iterator O(n) with out

    最后插入的时间复杂度取决于你是否有最后一个节点的位置,如果你有,那将是 O(1) 否则你将不得不搜索链表,时间复杂度将跳转到 O(n)。

    【讨论】:

    • 插入单向链表中间的复杂度是O(n)。如果列表是双向链接的,并且您知道要在其中插入的节点是 O(1)
    • 我忘了添加迭代器部分。谢谢指出
    • @Rob:这可能是一个愚蠢的疑问,但我无法理解如何在 O(1) 中插入双向链表?如果我有1 <-> 2 <-> 3 <-> 4 并且如果我必须在 3 和 4 之间插入 5,并且我所拥有的只是指向头节点的指针(即 1),我必须在 O(n) 中遍历。我错过了什么吗?
    • 如果您知道需要插入的索引,则插入双向链表的时间复杂度为 O(1)。如果不这样做,则必须遍历所有元素,直到找到所需的元素。双向链表具有数组和列表的所有优点:它们可以在 O(1) 中添加并在 O(1) 中删除,前提是您知道索引。如果要插入/删除的索引未知,则需要 O(n)。请注意,如果您的列表未排序,则查找元素总是需要 O(n) (否则为 log(n))
    • @FuriousFolder :即使它知道索引,例如位置 5,指针如何仍以恒定的时间到达那里以执行插入/删除操作?我仍然无法理解这个概念。
    【解决方案3】:

    红黑树:

    • 插入 - O(log n)
    • 检索 - O(log n)
    • 删除 - O(log n)

    【讨论】:

      【解决方案4】:

      请记住,除非您正在编写自己的数据结构(例如 C 中的链表),否则它可能很大程度上取决于您选择的语言/框架中数据结构的实现。例如,看看benchmarks of Apple's CFArray over at Ridiculous Fish。在这种情况下,数据类型(来自 Apple 的 CoreFoundation 框架的 CFArray)实际上会根据数组中实际存在的对象数量来更改数据结构 - 在大约 30,000 个对象时从线性时间变为恒定时间。

      这实际上是面向对象编程的美妙之处之一——您不需要知道它是如何工作的,只需要知道它是如何工作的,并且'它的工作原理可以根据要求而改变。

      【讨论】:

        【解决方案5】:

        没有比这更有用的了:Common Data Structure Operations:

        【讨论】:

          【解决方案6】:

          哈希表的 Amortized Big-O:

          • 插入 - O(1)
          • 检索 - O(1)
          • 删除 - O(1)

          请注意,哈希算法有一个常数因子,摊销意味着实际测量的性能可能会有很大差异。

          【讨论】:

          • 什么是向哈希集中插入 N 个项目的 Big-O?三思而后行。
          • 摊销,它是 N。不过,您可能在调整支持数组的大小时遇到​​问题。此外,这取决于您处理冲突的方法。如果您进行链接并且您的链接插入算法是 N(就像在单链表的尾部),它可以分解为 N^2。
          • 这是错误的。您对“摊销”的定义错误。摊销意味着执行一堆操作的总时间除以操作数。插入 N 个项目的最坏情况性能肯定是 O(N^2),而不是 O(N)。所以上面的操作仍然是 O(n) 最坏情况,摊销与否。假设散列函数的分布为 O(1),您会将其与“平均”时间复杂度混淆。
          • 他们仍然告诉人们哈希表的插入/检索/删除是 O(1),即使调整自身大小的哈希表肯定不会在触发调整大小的插入。我一直听说这被解释为摊销。你怎么称呼它?
          猜你喜欢
          • 2011-11-09
          • 2016-10-14
          • 2011-04-23
          • 1970-01-01
          • 1970-01-01
          • 2014-05-01
          • 2022-01-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多