哪个STL容器最适合std :: sort? (这甚至重要吗?)

时间:2009-03-31 23:45:18

标签: c++ stl sorting

标题不言自明......

容器的选择是否会以某种方式影响默认的std :: sort算法的速度?例如,如果我使用list,排序算法是仅切换节点指针还是切换节点中的整个数据?

9 个答案:

答案 0 :(得分:14)

选择确实有所作为,但预测哪个容器最有效是非常困难的。最好的方法是使用最容易让你的应用程序使用的容器(可能是std :: vector),查看该容器的排序是否足够快,如果是这样的话,那就坚持下去。如果没有,请对排序问题进行性能分析,并根据配置文件数据选择不同的容器。

作为一名前讲师和前培训师,我有时会对链接列表具有神秘性能增强属性这一共同观念负责。从一个知道的人那里得到它:链接列表出现在如此多的教科书和教程中的唯一原因是因为它对于编写这些书籍和教程的人来说很方便,它具有可以说明指针,动态内存管理,递归的数据结构,搜索和排序一体 - 它与效率无关。

答案 1 :(得分:12)

我认为std::sort不适用于列表,因为它需要一个list<>未提供的随机访问迭代器。请注意,list<>提供了sort方法,但它与std::sort完全分开。

容器的选择很重要。 STL的std::sort依靠迭代器来抽象容器存储数据的方式。它只是使用您提供的迭代器来移动元素。迭代器在访问和分配元素方面的工作速度越快,std::sort的工作速度就越快。

答案 2 :(得分:8)

std::list绝对不是std::sort()的好(有效)选择,因为std::sort()需要随机访问迭代器。 std::map和朋友也不好,因为元素的位置无法执行;也就是说,用户在插入特定位置或交换时无法强制执行地图中元素的位置。在标准容器中,我们只有std::vectorstd::deque

std::sort()与其他标准算法类似,只是通过交换(*t = *s)元素的值来实现。因此,即使列表神奇地支持O(1)访问,链接也不会被重组,而是它们的值将被交换。

由于std::sort()不会更改容器的大小,因此无论您使用std::vector还是std::deque,都不会对运行时性能产生任何影响。原始数组也应该快速排序,甚至可能比标准容器更快 - 但我不认为速度的差异足以证明使用它们的合理性。

答案 3 :(得分:5)

取决于元素类型。

如果您只是存储指针(或POD),那么矢量将是最快的。如果你正在存储对象,那么列表的排序会更快,因为它会交换节点而不是物理元素。

答案 4 :(得分:1)

排序算法对您的容器一无所知。它所知道的只是随机访问迭代器。因此,您可以对甚至不在STL容器中的事物进行排序。所以它的速度有多快取决于你给它的迭代器,以及取消引用和复制它们所指向的速度有多快。

std :: sort将无法在std :: list上运行,因为sort需要随机访问迭代器。对于该情况,您应该使用std :: list的成员函数排序之一。这些成员函数将有效地交换链表指针而不是复制元素。

答案 5 :(得分:1)

矢量。

始终使用矢量作为默认值。它具有最低的空间开销和任何其他容器的最快访问权限(以及其他优点,如C兼容布局和随机访问迭代器)。

现在,问问自己 - 你用容器做了什么?您需要强有力的例外保证吗?列表,集合和映射可能是更好的选择(尽管它们都有自己的排序例程)。您是否需要定期在容器前面添加元素?考虑一下deque。您的容器是否需要始终进行排序?设置和地图可能更合适。

最后,具体确定“最佳”是什么,然后选择最合适的容器,并根据您的需求衡量它的表现。

答案 6 :(得分:1)

我完全同意上面发布的声明。但是学习新事物的最佳方式是什么?嘿!!!!肯定不是阅读文本和学习心脏但是......实例:D最近我沉浸在STL中指定的容器中,这里是快速测试代码,不言自明,我希望:

#include <iostream>
#include <vector>
#include <deque>
#include <array>
#include <list>
#include <iterator>
#include <cstdlib>
#include <algorithm>
#include "Timer.h"

constexpr int SIZE = 1005000;

using namespace std;

void test();

int main(){
    cout<<"array allocates "<<static_cast<double>(SIZE)/(1024*1024)<<" MB\n";
    test();


    return 0;
}


void test(){
    int values[SIZE];
    int size = 0;

    //init values to sort:
    do{
        values[size++] = rand() % 100000;
    }while(size < SIZE);

    //feed array with values:
    array<int, SIZE> container_1;
    for(int i = 0; i < SIZE; i++)
        container_1.at(i) = values[i];

    //feed vector with values
    vector<int> container_2(begin(values), end(values));
    list<int> container_3(begin(values), end(values)); 
    deque<int> container_4(begin(values), end(values)); 

    //meassure sorting time for containers
    {
       Timer t1("sort array");
       sort(container_1.begin(), container_1.end());
    }

    {
       Timer t2("sort vector");
       sort(container_2.begin(), container_2.end());
    }

    {
       Timer t3("sort list");
       container_3.sort();
    }

    {
       Timer t4("sort deque");
       sort(container_4.begin(), container_4.end());
    }

}

计时码的代码:

#include <chrono>
#include <string>
#include <iostream>

using namespace std;

class Timer{

public:
    Timer(string name = "unnamed") : mName(name){ mStart = chrono::system_clock::now();}
    ~Timer(){cout<<"action "<<mName<<" took: "<<
             chrono::duration_cast<chrono::milliseconds>(
                     chrono::system_clock::now() - mStart).count()<<"ms"<<endl;}
private:
    chrono::system_clock::time_point mStart;
    string mName;
};

以下是未使用优化时的结果( g ++ --std = c ++ 11 file.cpp -o a.ou t):

数组分配0.958443 MB
动作排序数组:183ms
动作排序矢量:316ms
行动排序清单:725ms
行动排序deque花了:436ms

并进行优化( g ++ -O3 --std = c ++ 11 file.cpp -o a.out ):

数组分配0.958443 MB
动作排序数组:55ms
动作排序矢量:57ms
动作排序列表:264ms
行动排序deque花了:67ms

请注意,虽然向量和数组在这种情况下的排序次数相似,但是数组大小是有限的,因为它应该在堆栈上初始化(默认情况下,不使用自己的分配器等)。

所以这也取决于你是否对编译器使用优化,如果没有,我们可能会看到明显的差异。

答案 7 :(得分:0)

这肯定很重要,因为不同的容器具有不同的内存访问模式等可能起作用。

但是,std::sort不适用于std::list<>::iterators,因为这些不是RandomAccessIterators。此外,虽然可以实现std::list<>的特化,这会使节点的指针混乱,但它可能会产生奇怪且令人惊讶的语义后果 - 例如。如果你在向量中的排序范围内有一个迭代器,它的值将在排序后改变,这对于这个特化是不正确的。

答案 8 :(得分:0)

std :: sort需要随机访问迭代器,因此您使用它的唯一选项是vector或deque。它将交换值,并且在猜测向量中可能比deque执行稍快,因为它通常具有更简单的底层数据结构。但差异可能很小。

如果使用std :: list,则应该有一个特殊化(std :: list :: sort),它应该交换指针而不是值。但是因为它不是随机访问,所以它将使用mergesort而不是quicksort,这可能意味着算法本身有点慢。

无论如何,我认为答案通常是矢量。如果每个元素都有大类,那么复制开销将主导排序过程,列表可能会超过它。或者,您可以在向量中存储指向它们的指针,并提供自定义谓词以对它们进行适当的排序。