C++ 哪个STL容器最适合std::sort?(这有关系吗?)
标题不言而喻C++ 哪个STL容器最适合std::sort?(这有关系吗?),c++,stl,sorting,C++,Stl,Sorting,标题不言而喻 容器的选择是否会以某种方式影响默认std::sort算法的速度?例如,如果我使用list,排序算法是只切换节点指针还是切换节点中的整个数据?这取决于元素类型 若你们只是存储指针(或POD),那个么向量将是最快的。如果您正在存储对象,则列表的排序将更快,因为它将交换节点而不是物理元素。我认为std::sort不适用于列表,因为它需要一个随机访问迭代器,而列表不提供该迭代器。请注意,list提供了一个sort方法,但它与std::sort完全分离 容器的选择很重要。STL的std::s
容器的选择是否会以某种方式影响默认std::sort算法的速度?例如,如果我使用list,排序算法是只切换节点指针还是切换节点中的整个数据?这取决于元素类型
若你们只是存储指针(或POD),那个么向量将是最快的。如果您正在存储对象,则列表的排序将更快,因为它将交换节点而不是物理元素。我认为
std::sort
不适用于列表,因为它需要一个随机访问迭代器,而列表不提供该迭代器。请注意,list
提供了一个sort
方法,但它与std::sort
完全分离
容器的选择很重要。STL的std::sort
依赖于迭代器来抽象容器存储数据的方式。它只使用您提供的迭代器来移动元素。这些迭代器访问和分配元素的速度越快,std::sort
的工作速度就越快。排序算法对容器一无所知。它只知道随机访问迭代器。因此,您可以对STL容器中没有的内容进行排序。所以它的速度取决于你给它的迭代器,以及它去引用和复制它们指向的内容的速度
由于排序需要随机访问迭代器,所以std::sort在std::list上不起作用。对于这种情况,应该使用std::list的成员函数排序之一。这些成员函数将有效地交换链表指针,而不是复制元素。std::list
对于std::sort()
,绝对不是一个好的(有效的)选择,因为std::sort()
需要随机访问迭代器<代码>标准::地图
和朋友也不好,因为元素的位置无法强制执行;也就是说,用户无法通过插入到特定位置或交换来强制地图中元素的位置。在标准容器中,我们可以使用std::vector
和std::deque
std::sort()
与其他标准算法类似,它只通过交换元素的值来起作用(*t=*s
)。所以,即使列表神奇地支持O(1)访问,链接也不会被重新组织,而是它们的值会被交换
由于
std::sort()
不会更改容器的大小,因此无论使用std::vector
还是std::deque
,都不会影响运行时性能。基本数组的排序速度也应该很快,可能甚至比标准容器更快——但我不认为速度上的差异足以证明使用它们是合理的。这当然很重要,因为不同的容器具有不同的内存访问模式等,这可能会起到一定的作用
但是,
std::sort
不适用于std::list::迭代器
,因为它们不是随机访问迭代器。此外,尽管可以实现对std::list
的专门化,以洗牌节点的指针,但它可能会产生奇怪和令人惊讶的语义后果-例如,如果向量中的排序范围内有一个迭代器,其值将在排序后改变,这在这种专门化中是不正确的。选择确实会产生影响,但是预测哪个容器将是最有效的是非常困难的。最好的方法是使用应用程序最容易使用的容器(可能是std::vector),查看该容器的排序速度是否足够快,如果足够快,请使用它。如果没有,请对排序问题进行性能分析,并根据分析数据选择不同的容器
作为一名前讲师和前培训师,我有时觉得自己应该对一个普遍的想法负责,即链表具有神秘的性能提升特性。从一个知道的人那里可以看出:链表出现在如此多的教科书和教程中的唯一原因是,编写这些书籍和教程的人渴望拥有一个能够说明指针、动态内存管理、递归、,搜索和排序集于一身-这与效率无关。std::sort需要随机访问迭代器,因此您唯一可以使用的选项是vector或deque。它将交换这些值,并且在猜测时,vector的执行速度可能会比deque稍快,因为它通常具有更简单的底层数据结构。不过,两者之间的差异可能非常微小 如果使用std::list,则有一个专门化(std::list::sort),它应该交换指针而不是值。然而,由于它不是随机访问,它将使用mergesort而不是quicksort,这可能意味着算法本身会稍微慢一点 无论如何,我认为答案通常是向量。如果每个元素都有大型类,所以复制开销在排序过程中占主导地位,列表可能会胜过它。或者,您也可以将指向它们的指针存储在向量中,并提供自定义谓词以对它们进行适当排序。vector 始终使用vector作为默认值。它具有最低的空间开销和最快的访问速度(以及其他优势,如C兼容布局和随机访问迭代器) 现在,问问你自己——你还用你的容器做什么?你需要强有力的例外保证吗?列表、集合和映射可能是更好的选项(尽管它们都有自己的排序例程)。您是否需要定期向容器正面添加元素?考虑一下迪克。您的集装箱是否需要一直分拣?Set和map可能更适合 最后,明确指出什么是“最好的”对你来说,然后再选择cho
#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;
};