我正在寻找一个像std :: list这样的std容器,可以有效地将元素移到前面:
a-b-c-d-e
将“b”移到前面:
a-c-d-e-b
std容器中没有这样的功能。因此,我认为我必须结合使用remove和push_front函数,但任何人都可以找到更好的主意吗?
提前感谢。
答案 0 :(得分:15)
在std::vector
上,您可以使用具有线性复杂度的std::rotate
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
std::vector<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
// swap ranges [1, 2) and [2, 5)
auto it = std::next(v.begin(), 1); // O(1)
auto rb = std::next(it);
auto re = v.end();
std::rotate(it, rb, re); // O(N)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
在std::list
上你可以使用成员函数splice
,它(给定迭代器)具有恒定的复杂性
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
std::list<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
auto it = std::next(v.begin(), 1); // O(N)
auto rb = std::next(it);
auto re = v.end();
v.splice(it, v, rb, re); // O(1)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
注意:最后一个元素在STL容器中通常表示为back
,第一个元素表示为front
。对于std::vector
,获取某个元素的迭代器是常量时间,交换是线性时间。对于std::list
,获取迭代器是线性时间,但拼接到同一列表中的是常量时间。但是,矢量的更好的内存缓存行为也很重要,正如Stroustrup的 this benchmark 所示。
更新:一些评论者提到只是交换元素:这仅适用于您希望将a-b-c-d-e
转换为a-e-c-d-b
的情况。在这种情况下,请在您喜欢的任何容器上使用std::iter_swap
。要将a-b-c-d-e
转换为a-c-d-e-b
,请使用std::rotate
或list::splice
。
答案 1 :(得分:12)
如果您不必维护其他元素的顺序, 那么最简单的解决方案无疑只是交换了 您想要的元素与容器中的第一个元素。这个 所有容器都能提高效率。
否则,std::list
会提供splice
操作
使用。我认为如下所示:
void
moveToFront(
std::list<MyType>& list,
std::list<MyType>::iterator element )
{
if ( element != list.begin() ) {
list.splice( list.begin(), list, element, std::next( element ) );
}
}
这应该只有几个指针操作,并且
没有副本。另一方面,std::list
可能非常慢
一般(因为地方不好);我的评价非常
使用std::vector
仔细反对幼稚的实现,
确保它在全球范围内取得胜利。在这里删除所有副本
如果迭代找到你想要的元素,可能不是一个胜利
搬到前面十倍更贵。 (很多这个
取决于复制MyType
的费用,以及它的大小
是。如果sizeof(MyType)
接近页面大小,或者
访问MyType
最终间接访问了很多
分配的对象,locality参数将不成立。)
使用std::vector
,而不是显而易见的erase
/ insert
void
moveToFront(
std::vector<MyType>& list,
std::vector<MyType>::iterator element )
{
MyType tmp( *element );
std::copy_backwards( list.begin(), std::prev( element ), element );
*list.begin() = tmp;
}
这将导致副本少于erase
(副本
所有以下元素)insert
(也复制所有元素
以下元素 - 这意味着所有元素,
因为我们在开头插入模式。