为什么矢量前没有推/弹?

时间:2017-02-05 12:27:08

标签: c++ vector stl

在C ++,STL中,我们有模板类<vector>。 我们知道它支持O(1)随机访问和尾部修改。 我的问题是为什么我们不在<vector>中定义push_front或pop_front?

一种解释是,如果我们想在向量的前面推/弹元素,我们必须将数组中的每个元素移动一步,这将花费O(n)

但我认为并非总是如此。考虑到如果我们使用循环数组实现<vector>,我们可以从向量的前端和尾部实现O(1)推送/弹出,而不会失去O(1)随机访问的能力。所以我个人认为没有任何理由,而不仅仅是一个小的开销,而不是为push_front实施pop_front / <vector>。有什么想法吗?

3 个答案:

答案 0 :(得分:8)

  

一种解释是,如果我们想在向量的前面推/弹元素,我们必须将数组中的每个元素移动一步,这将花费O(n)

你是绝对正确的,push_front无法快速运行,因为除了可能的重新分配之外,所有项目都需要复制一个位置。这为n个对象提供了O(n 2 )的摊销性能,这不是图书馆设计者想要鼓励的。

  

考虑到我们实现<vector>圆形数组

使用循环数组实现向量使得实现几个对于向量必须为true的重要保证变得更加困难。例如,vector必须保证如果迭代器a指向索引低于迭代器b的元素,则a < b。当vector是线性的时,比较归结为比较迭代器ab所指向的元素的地址。使用循环数组实现时,需要考虑向量原点的地址,现在可以在分配的内存块中间。

另一个保证会被违反的是:

  

vvector<T>时,Tbool以外的任何类型,n是零和矢量大小之间的数字{{1}身份必须是真的。

使用圆形数组无法实现。

答案 1 :(得分:4)

实际上,这个 是一个现实的要求。据我所知,标准中没有任何内容要求向量在元素(v.prefix_capacity())之前不能有缓冲,就像之后的v.capacity() - v.size() )。这可以保证v.push_front()v.push_back()的运行时间相同,而对于那些不使用它的用户则没有任何成本。此外,它可以保证O(1)v.pop_front(),尽管通过使迭代器无效。想写一个提案吗?

同时,你可以用vector创建一个模板(devector?),其中:

  • 的字段pre_capacity_已初始化为0,而getter pre_capacity()
  • 实现pre_reserve(size_t i),它们同时调用:
    • reserve(capacity() - pre_capacity_ + i)
    • pre_capacity_ += i
  • 实施operator[](size_t i)并委托给v[i + pre_capacity()]
  • 实施at(size_t i)并委托给v.at(i + pre_capacity())
  • 实施begin()并委托给v.begin() + pre_capacity()
  • 将所有其他方法委托给vector

或者您可以跟踪您从前面推送/弹出的元素数量:)。

答案 2 :(得分:0)

实施去矢量化带来两个挑战:

  • 保留O(1)随机访问权限(与deque不同)。
  • O(1)向两侧(正面或背面)推动操作,这是双端队列的好处。

这两种方法都可以通过以下方式实现:

  • 实现一个devector类,该类从std :: vector公开继承。因此,我们有std :: vector API可用,“只是”需要覆盖需要覆盖的方法。
  • 继承的向量(基类)将用于常规的push_back插入。
  • devector类还将具有一个私有的std :: vector成员,让我们将其称为 front_insertions
  • 对devector的任何push_front都会对 front_insertions (私有成员向量)执行push_back。
  • devector的大小将是两个大小的总和:继承的向量和 front_insertions 的大小。
  • 对索引i的元素的随机访问将以以下方式实现: 如果 front_insertions 为空,我们将按原样重用继承的随机访问方法。否则,如果我< front_insertions.size(),我们将访问 front_insertions [front_insertions.size()-1-i] 。否则,我们访问继承向量的 [i-front_insertions.size()]
  • 显然,可以重写其他一些std :: vector方法,例如emplace_back,emplace_front,at等。我专注于保留O(1)的随机访问权,并推向任一侧。