std:container c ++移到前面

时间:2013-01-29 09:49:18

标签: c++ list stl vector swap

我正在寻找一个像std :: list这样的std容器,可以有效地将元素移到前面:

a-b-c-d-e

将“b”移到前面:

a-c-d-e-b

std容器中没有这样的功能。因此,我认为我必须结合使用remove和push_front函数,但任何人都可以找到更好的主意吗?

提前感谢。

2 个答案:

答案 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::rotatelist::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(也复制所有元素 以下元素 - 这意味着所有元素, 因为我们在开头插入模式。