除了使用以下内容之外,还有其他任何时间方法来分割矢量。
std::vector<int> v_SplitVector(start , end);
这将需要O(N)的复杂性。在这种情况下O(结束 - 开始)。是否有恒定的时间操作。
或我使用了错误的容器来完成任务吗?..
答案 0 :(得分:3)
这是一个副本而不是分裂,因此复杂。您可以为list
编写一个可能表现更好的分组。
答案 1 :(得分:3)
“拆分”容器的行为,对于像矢量这样的容器,其中元素位于连续的内存中,需要复制/移动所有需要在另一端的内容。
像list
这样的容器,每个容器都有自己的内存块,可以很容易地重新排列(参见std::list::splice
)
但是,由于更频繁的缓存丢失,在非连续内存中使用元素可能会导致内存访问性能降低。
换句话说,算法的复杂性可能不是影响性能的唯一因素:不常见的线性副本可能损坏,而不是频繁的分散元素线性行走。
权衡取决于硬件如何管理缓存以及您使用的std实现如何处理(以及编译器最终如何优化)
答案 2 :(得分:2)
std::vector
不支持以下内容,但如果高效的“拆分”操作对您来说非常重要,那么您可以编写自己的容器。这将是相当多的工作。
您可以按如下方式定义“拆分”:
然后旧容器和新容器将共享一个底层存储块(可能是重新计数)。如果你追加它,新容器将不得不重新分配(因为它的元素末尾的内存正在使用中),但只要这种情况很少发生或永远不会是胜利。
您的示例代码需要一份副本,但它不会修改原始容器。如果需要逻辑副本,那么在不实际复制元素的情况下执行它,您需要COW或不可变对象。
std::list
有一个splice()
函数,可以将一系列元素从一个列表移动到另一个列表。这避免了复制元素,但从C ++ 11开始,它实际上保证不是O(1)
,因为它需要计算它移动了多少元素。在C ++ 03中,实现可以选择是否希望此操作为O(1)
或list::size()
为O(1)
,但在C ++ 11 size()
中需要保持不变所有容器的时间。
将std::vector
与std::list
的效果进行比较通常不仅仅是一项操作。您必须考虑list
没有随机访问迭代器,依此类推。
答案 3 :(得分:2)
创建新的std::vector
必然需要复制,因为
不允许向量共享其实现的部分内容。
您从中获得start
容器的修改
并且end
不应影响splitVector
中的值。
您可以轻松地创建一个View
容器,
它只保存两个迭代器,并映射所有访问
通过他们。有点像:
template <typename Iterator>
class View
{
Iterator myBegin;
Iterator myEnd;
public:
typedef typename std::iterator_traits<Iterator>::value_type value_type;
// And the other usual typedefs, including...
typedef Iterator iterator;
View( Iterator begin, Iterator end )
: myBegin( begin )
, myEnd( end )
{
}
iterator begin() { return myBegin; }
iterator end() { return myEnd; }
value_type operator[]( ptrdiff_t index ) { return *(myBegin + index ); }
// ...
};
这需要相当数量的样板,因为
像vector这样的东西的界面相当完整,但它是
一切都非常直接和简单。你不能做的一件事
但是,要做的是修改其中的拓扑
底层容器或任何View
- 任何可能的东西
当然,使任何迭代器失效都会造成破坏。
答案 4 :(得分:0)
在不同于开始/结束的位置添加元素或从中删除元素时,由于需要内部移位,向量必须至少具有o(n)的复杂度。如果您不仅要移除元素,而且要移出元素,则会出现以下情况:对于向量,必须复制它们,因此,每个元素移动至少1个操作。这意味着从向量中移动元素至少为O(N),其中N是移动元素的数量。
如果你需要近乎恒定的时间添加/删除操作(无论是添加/插入一个或多个元素),你应该查看list / linkedlist容器,其中所有元素和子列表都很容易“可拆卸”,特别是如果你知道的话指针/迭代器。或树木,或任何其他动态结构。
完全顺便说一下,我感觉v_SplitVector
做了什么,但是它来自哪里?我不记得stdlib或boost中的这种函数/方法吗?