【问题标题】:OpenDataStructures: ArrayQueueOpenDataStructures:数组队列
【发布时间】:2017-01-19 00:11:05
【问题描述】:

我正在自学算法并转向Open Data Structures(C++ 版)免费电子书作为参考。在我试图掌握这个主题的过程中,我决心完成书中的所有挑战。但是,我很难理解一种特定的不受欢迎的算法如何为 add() 和删除提供 O(1)。

其中一个挑战是使用这些属性创建一个随机队列 (exercises)。

练习 2.2。设计和实现一个随机队列。这是 Queue 接口的一个实现,其中 remove() 操作删除一个在当前队列中的所有元素中随机均匀选择的元素。 (将 RandomQueue 想象成一个袋子,我们可以在其中添加元素或伸手可及并盲目地删除一些随机元素。) RandomQueue 中的 add(x) 和 remove() 操作应该在每个操作中以恒定时间运行。

本章涉及array-backed-list,因此从这个意义上说,元素的添加和删除相当简单。但是,有时必须根据大小重新创建数组。您应该将旧数组复制到新数组。这基本上可以是 O(n)。另外我相信我需要使用一个圆形数组。所以基本上我必须在数组中移动索引以产生 O(n-1) 的时间复杂度。

我对如何计算和测量这些算法感到非常困惑。这本书确实谈到了 O(m),但有时相当模糊。

定理 2.2。 ArrayQueue 实现(FIFO)队列接口。忽略调用 resize() 的成本,ArrayQueue 在 O(1) 时间内支持操作 add(x) 和 remove() 。此外,从一个空的 ArrayQueue 开始,任何 m 个 add(i, x) 和 remove(i) 操作序列都会导致在所有对 resize() 的调用期间总共花费 O(m) 时间。

就像你怎么能忽略这一点一样,我并没有与你如何能去掉那部分时间复杂度的联系?

【问题讨论】:

标签: c++ arrays algorithm data-structures time-complexity


【解决方案1】:

调整大小通常计划以指数分布的大小发生。比如说,当大小为 8 时,获得一个大小为 16 的存储,将内容移到那里并丢弃旧的大小为 8 的存储。当它填满时,获得一个大小为 32 的存储空间,依此类推。这样,插入n 元素的摊销复杂度仍然是O(n):我们总共移动了元素n + n/2 + n/4 + n/8 + ... 次,它以2n 为界,因此是线性的。

如果在结构收缩时需要调整大小,也可以这样做:当一个 size-16 的结构只包含 4 个元素时,获取一个 size-8 的存储,将内容移到那里并删除旧的 size-16 的存储。

2 的幂可以用其他数字的幂代替,例如 1.5。权衡是浪费了多少内存与移动内容花费了多少时间。两者都是O(n),但我们可以提高其中一个的常数因子并降低另一个。

也许本书现在还不想处理摊销的复杂性,因此避免讨论内存管理的细节。

【讨论】:

  • 因此,即使这些方法具有摊销复杂性,它们仍然可以被视为 0(1)?
  • @Aaron 正确的说法是:n 插入在O(n) 总操作中完成。然后我们说每个插入的摊销成本为O(1)。这正是我们的意思:当发生调整大小时,一个单独的插入仍然可能与 O(n) 一样昂贵,但这样的峰值很少见,并且在许多其他廉价插入中被摊销。
  • 如果分配本身被认为是常数时间,那么这些类型的数组列表可以被摊销以提供真正的 O(1) 成本。当旧数组填满时分配一个新数组,然后在插入新项时将项从旧数组逐步移动到新数组。这个技巧在文献中经常使用,让作者使用固定大小的数据结构来证明目标的复杂性。
猜你喜欢
  • 2013-11-09
  • 1970-01-01
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 2020-06-12
  • 2017-05-06
  • 2011-03-20
相关资源
最近更新 更多