簡體   English   中英

哪個 STL 容器最適合 std::sort? (這還重要嗎?)

[英]Which STL container is best for std::sort? (Does it even matter?)

標題不言自明......

容器的選擇是否會以某種方式影響默認 std::sort 算法的速度? 例如,如果我使用列表,排序算法是只切換節點指針還是切換節點中的整個數據?

選擇確實會產生影響,但預測哪個容器最有效是非常困難的。 最好的方法是使用對您的應用程序來說最容易使用的容器(可能是 std::vector),看看該容器的排序是否足夠快,如果是的話,堅持使用它。 如果沒有,請對您的排序問題進行性能分析,並根據配置文件數據選擇不同的容器。

作為一名前講師和前培訓師,我個人有時會為鏈表具有神秘的性能增強特性這一普遍觀點負責。 知道的人說一下:鏈表出現在這么多教科書和教程中的唯一原因是因為編寫這些書和教程的人有一個可以說明指針、動態 memory 管理、遞歸的數據結構很方便,搜索和排序都在一個 - 它與效率無關。

我認為std::sort不適用於列表,因為它需要一個list<>不提供的隨機訪問迭代器。 請注意, list<>提供了一種sort方法,但它與std::sort完全分開。

容器的選擇很重要。 STL 的std::sort依賴於迭代器來抽象出容器存儲數據的方式。 它只是使用您提供的迭代器來移動元素。 這些迭代器在訪問和分配元素方面工作得越快, std::sort工作得越快。

std::list絕對不是std::sort()的好(有效)選擇,因為std::sort()需要隨機訪問迭代器。 std::map和朋友也不好,因為不能強制執行元素的 position; 也就是說,用戶不能通過插入特定的 position 或交換來強制執行 map 中元素的 position。 在標准容器中,我們只剩下std::vectorstd::deque

std::sort()與其他標准算法一樣,它僅通過在 ( *t = *s ) 周圍交換元素的值來起作用。 因此,即使列表神奇地支持 O(1) 訪問,鏈接也不會被重新組織,而是它們的值會被交換。

因為std::sort()不會改變容器的大小,所以無論您使用std::vector還是std::deque都應該不會影響運行時性能。 原始 arrays 的排序速度也應該很快,甚至可能比標准容器還要快——但我不認為速度上的差異足以證明使用它們是合理的。

這取決於元素類型。

如果您只是存儲指針(或 POD),那么 vector 將是最快的。 如果您要存儲對象,那么列表的排序會更快,因為它將交換節點而不是物理元素。

我完全同意上面張貼的聲明。 但是學習新事物的最佳方法是什么? 嘿....當然不是閱讀文本並用心學習但是:,,: 示例: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.out ):

數組分配 0.958443 MB
動作排序數組耗時:183ms
動作排序向量耗時:316ms
動作排序列表耗時:725ms
動作排序雙端隊列花費:436ms

並進行優化( g++ -O3 --std=c++11 file.cpp -o a.out ):

數組分配 0.958443 MB
動作排序數組耗時:55ms
動作排序向量耗時:57ms
動作排序列表耗時:264ms
動作排序雙端隊列耗時:67ms

請注意,盡管在這種情況下向量和數組具有相似的時間排序,但數組大小是有限的,因為它應該在堆棧上初始化(默認情況下,不使用自己的分配器等)

所以這也取決於你是否對編譯器使用優化,如果沒有,我們可能會看到明顯的差異。

排序算法對您的容器一無所知。 它所知道的只是隨機訪問迭代器。 因此,您可以對 STL 容器中沒有的內容進行排序。 所以它的速度取決於你給它的迭代器,以及解引用和復制它們指向的東西的速度。

std::sort 不適用於 std::list,因為 sort 需要隨機訪問迭代器。 對於這種情況,您應該使用 std::list 的成員 function 排序之一。 這些成員函數將有效地交換鏈表指針,而不是復制元素。

向量。

始終使用矢量作為默認值。 它具有任何其他容器最低的空間開銷和最快的訪問速度(以及其他優點,如 C 兼容布局和隨機訪問迭代器)。

現在,問問你自己——你還用你的容器做什么? 您需要強大的異常保證嗎? List、set 和 map 可能是更好的選擇(盡管它們都有自己的排序例程)。 您是否需要定期將元素添加到容器的前面? 考慮雙端隊列。 您的容器是否需要始終進行分類? Set 和 map 可能更合適。

最后,具體找出最適合您的“最佳”容器,然后選擇最合適的容器並衡量它如何滿足您的需求。

這確實很重要,因為不同的容器有不同的 memory 訪問模式等,這可能會起作用。

但是, std::sort不適用於std::list<>::iterators ,因為它們不是 RandomAccessIterators。 此外,雖然有可能實現std::list<>的特化來打亂節點的指針,但它可能會產生奇怪和令人驚訝的語義后果 - 例如。 如果您在向量的排序范圍內有一個迭代器,則它的值將在排序后發生變化,這對於這種專業化來說是不正確的。

std::sort 需要隨機訪問迭代器,因此您唯一可以使用的選項是向量或雙端隊列。 它將交換值,並且猜測向量的執行速度可能比 deque 稍快,因為它通常具有更簡單的底層數據結構。 不過,差異可能非常微不足道。

如果您使用 std::list,則有一個專門化 (std::list::sort) 應該交換指針而不是值。 然而,由於它不是隨機訪問,它將使用合並排序而不是快速排序,這可能意味着算法本身要慢一些。

無論如何,我認為答案通常是矢量。 如果每個元素都有大型類,因此復制開銷支配了排序過程,那么 list 可能會擊敗它。 或者,您可以將指向它們的指針存儲在一個向量中,並提供一個自定義謂詞來對它們進行適當的排序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM