在C ++,STL中,我们有模板类<vector>
。
我们知道它支持O(1)
随机访问和尾部修改。
我的问题是为什么我们不在<vector>
中定义push_front或pop_front?
一种解释是,如果我们想在向量的前面推/弹元素,我们必须将数组中的每个元素移动一步,这将花费O(n)
。
但我认为并非总是如此。考虑到如果我们使用循环数组实现<vector>
,我们可以从向量的前端和尾部实现O(1)
推送/弹出,而不会失去O(1)
随机访问的能力。所以我个人认为没有任何理由,而不仅仅是一个小的开销,而不是为push_front
实施pop_front
/ <vector>
。有什么想法吗?
答案 0 :(得分:8)
一种解释是,如果我们想在向量的前面推/弹元素,我们必须将数组中的每个元素移动一步,这将花费O(n)
你是绝对正确的,push_front
无法快速运行,因为除了可能的重新分配之外,所有项目都需要复制一个位置。这为n个对象提供了O(n 2 )的摊销性能,这不是图书馆设计者想要鼓励的。
考虑到我们实现
<vector>
圆形数组
使用循环数组实现向量使得实现几个对于向量必须为true的重要保证变得更加困难。例如,vector必须保证如果迭代器a
指向索引低于迭代器b
的元素,则a < b
。当vector是线性的时,比较归结为比较迭代器a
和b
所指向的元素的地址。使用循环数组实现时,需要考虑向量原点的地址,现在可以在分配的内存块中间。
另一个保证会被违反的是:
当
v
是vector<T>
时,T
是bool
以外的任何类型,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)
实施去矢量化带来两个挑战:
这两种方法都可以通过以下方式实现: