• 文章
  • std::sort() 函式入門指南
2013 年 5 月 6 日 (最後更新:2013 年 6 月 1 日)

std::sort() 函式入門指南

評分:4.3/5 (1227 票)
*****

重要資訊


在開始之前,我想說明我將使用僅在 C++11 編譯器中可用的功能。如果您沒有 C++11 或不知道您的編譯器是否支援它,我建議您這樣做。前往 CodeBlocks 下載他們的 IDE。它帶有一個 C++11 編譯器,您可以透過轉到 settings->compiler->compiler settings->compiler flags-> 來啟用它,然後您應該會看到一個複選框,上面寫著“讓 g++ 遵循 C++11 ISO C++ 語言標準”。啟用它並點選“確定”即可。



它的樣子


algorithm 標頭檔案中的 sort() 函式對新手和經驗豐富的程式設計師來說都可能是一個非常有用的工具。它的用途是排序陣列和向量等容器。

第一個示例展示了函式的樣子。第二個示例是一個可選的過載函式,它包含第三個引數。首先,讓我們看一下這些函式,看看我們是否能弄清楚每個引數的作用。

示例 1 ~ std::sort(myvector.begin(), myvector.end())

示例 2 ~ std::sort(myvector.begin(), myvector.end(), myCompFunction)


關於函式


那麼,讓我們深入研究一下,找出每個函式的作用以及它為什麼這樣做。


包含在 ~ #include <algorithm>

引數 1 myvector.begin() ~ 第一個引數是您將放置一個迭代器(指標)指向您要排序的範圍中的第一個元素。sort 將包含該迭代器指向的元素。

引數 2 myvector.end() ~ 第二個引數幾乎與第一個引數一樣,但不是放置指向要排序的第一個元素的迭代器,而是放置指向最後一個元素的迭代器。一個非常重要的區別是搜尋不會包含此迭代器指向的元素。它是 [First,Last),這意味著它包含排序中的第一個引數,但不包含排序中的第二個引數。

引數 3 myCompFunction() 可選 ~ 這裡我只會給出簡短的描述,因為我稍後會更詳細地解釋這個引數。第三個引數用於定義如何進行搜尋。例如,如果您有一個包含三個不同變數的結構體,函式如何知道要排序哪個變數?或者它如何知道如何排序?這就是這個引數的作用。我稍後會對此進行更多解釋。

函式返回值 ~ 該函式不返回任何內容,因為它直接透過迭代器(指標)修改容器。


陣列示例


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// sort() Example using arrays.
// By Zereo 04/22/13
#include <iostream>
#include <algorithm>

using namespace std;

const int SIZE = 7;

int main()
{
    int intArray[SIZE] = {5, 3, 32, -1, 1, 104, 53};

    //Now we call the sort function
    sort(intArray, intArray + SIZE);

    cout << "Sorted Array looks like this." << endl;
    for (size_t i = 0; i != SIZE; ++i)
        cout << intArray[i] << " ";

    return 0;
}




需要知道的事情

當我們使用 sort 函式對陣列進行排序時,我們的引數看起來會與使用向量時略有不同。在上面的示例中,當我們傳遞 intArray 作為引數時,我們告訴函式從陣列的開頭開始排序。如果我們想從陣列的第二個元素開始排序,我們可以這樣做:sort(intArray + 1, intArray + SIZE);。因此,當我們對第二個引數執行 intArray + SIZE 時,我們告訴陣列排序到陣列的最後一個元素。


使用 C++11 簡化事物

我們可以使用 std::begin()std::end() 來使整個陣列的排序更加容易。std::begin() 將返回一個迭代器(指標)指向我們傳遞的陣列的第一個元素。而 std::end() 將返回一個迭代器(指標)指向我們傳遞的陣列的最後一個元素之後的一個位置。所以我們可以像這樣呼叫 sort 函式,傳遞 begin() 和 end():

sort(begin(intArray), end(intArray));


對向量和其他 STL 容器進行排序示例


警告:使用 C++11 功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Vector Sorting Example.
// By Zereo 04/22/13
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

int main()
{
    // Warning this type of initialization requires a C++11 Compiler
    vector<int> intVec = {56, 32, -43, 23, 12, 93, 132, -154};
    vector<string> stringVec = {"John", "Bob", "Joe", "Zack", "Randy"};

    // Sorting the int vector
    sort(intVec.begin(), intVec.end());

    for (vector<int>::size_type i = 0; i != intVec.size(); ++i)
        cout << intVec[i] << " ";

    cout << endl;

    // Sorting the string vector
    sort(stringVec.begin(), stringVec.end());

    // Ranged Based loops. This requires a C++11 Compiler also
    // If you don't have a C++11 Compiler you can use a standard
    // for loop to print your vector.
    for (string &s : stringVec)
        cout << s << " ";

    return 0;
}



需要知道的事情

首先,正如您所見,排序函式與在陣列上的工作方式幾乎相同,只是我們傳遞引數的方式略有不同。由於 sort() 中的第一個引數接受指向我們要排序的第一個元素的迭代器(指標),因此我們可以將 stringVec.begin() 傳遞給它,因為 .begin() 返回指向第一個元素的迭代器。因此,它將從向量的第一個元素開始排序。對於第二個引數 stringVec.end() 也是如此,因為請記住 .end() 是一個指向容器中最後一個元素之後的一個位置的迭代器。請記住,sort 函式排序到但不包括我們作為第二個引數傳遞的內容。

您可能還注意到 sort 函式可以處理數字以外的其他內容。當我們列印字串向量時,它為我們提供了一個整潔的向量,其中按字母順序排列了姓名。



帶第三個引數的過載 sort()。


sort() 函式中的第三個引數實際上是一個非常有用的功能。它允許我們定義 sort() 函式實際執行搜尋的方式。有時您可以使用常規版本的 sort() 來完成,但是如果我們想透過讓容器按降序而不是升序排序來更改容器的排序方式該怎麼辦?或者如果我們有一個包含我們建立的特殊型別類物件的容器,並且需要以特殊方式對其進行排序該怎麼辦?那麼這就是第三個引數的用武之地。



使其按降序排序的示例。


警告:使用 C++11 功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Vector Sorting Descending Example.
// By Zereo 04/22/13
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

// We need this function to define how to sort
// the vector. We will pass this function into the
// third parameter and it will tell it to sort descendingly.
bool wayToSort(int i, int j) { return i > j; }

int main()
{
    vector<int> intVec = {56, 32, -43, 23, 12, 93, 132, -154};
    
    // Do not include the () when you call wayToSort
    // It must be passed as a function pointer or function object
    sort(intVec.begin(), intVec.end(), wayToSort);

    for (int i : intVec)
        cout << i << " ";
    
    return 0;
}



該函式

首先,讓我們看一下函式。我們所做的是建立一個函式,該函式將在每次呼叫時確定 i > j。sort 函式會自動為 i 和 j 分配一個元素。

您建立的函式需要具有布林返回型別。

因此,當我們定義 bool wayToSort(int i, int j) { return i > j; } 時,我們說我們希望它按降序排序,因為 i>j。而升序則是 i<j。


使用 STL 簡化升序或降序排序。

解決讓它按降序排序問題的另一種方法是使用 std::greater(),如下所示。

sort(intVec.begin(), intVec.end(), greater<int>());


排序使用者自定義型別。


對於許多程式,我們儲存的不僅僅是 ints、strings 或 doubles。相反,我們建立具有多個數字和字串成員的複雜類,並將它們儲存在容器中。因此,當我們想要對該類物件容器進行排序時,我們需要定義一個特殊函式來告訴 sort() 函式它應該如何排序這些物件。

所以,在我最後的例子中,假設我們有一個表示人的結構體,它看起來像這樣。

1
2
3
4
5
6
struct Person
{
    string name;
    int age;
    string favoriteColor;
};


您可以看到它有三個成員:name、age 和 color。現在,假設我們有一個包含 Person 物件的向量的程式,我們需要一種方法能夠在程式的特定點按其姓名、年齡或喜歡的顏色對它們進行排序。

一種方法是為每種不同的排序方式建立一個函式,就像下面的示例一樣。但這並非唯一的方法。

警告:使用 C++11 功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Complicated Types Sorting Example.
// By Zereo 04/22/13
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

struct Person
{
    // Left out making a constructor for simplicity's sake.
    string name;
    int age;
    string favoriteColor;
};

// Sort Container by name function
bool sortByName(const Person &lhs, const Person &rhs) { return lhs.name < rhs.name; }

// Sort Container by age function
bool sortByAge(const Person &lhs, const Person &rhs) { return lhs.age < rhs.age; }

// Sort Container by favorite color
// We can just sort alphabetically and then it will group the
// color together.
bool sortByColor(const Person &lhs, const Person &rhs) { return lhs.favoriteColor < rhs.favoriteColor; }

// A global const variable to hold how many people to ask for input for.
const unsigned numberOfPeople = 2;

int main()
{
    // Make a vector that holds 5 blank Person Objects
    vector<Person> people(numberOfPeople);

    // This will ask for user input to populate the container
    // with 5 different indivuals.
    for (vector<Person>::size_type i = 0; i != numberOfPeople; ++i)
    {
        cout << "Person #" << i + 1 << " name: ";
        cin >> people[i].name;

        cout << "Person #" << i + 1 << " age: ";
        cin >> people[i].age;

        cout << "Person #" << i + 1 << " favorite color: ";
        cin >> people[i].favoriteColor;
    }

    cout << "\n\n";

    // Sort by name
    sort(people.begin(), people.end(), sortByName);
    for (Person &n : people)
        cout << n.name << " ";

    cout << endl;

    // Sory by age
    sort(people.begin(), people.end(), sortByAge);
    for (Person &n : people)
        cout << n.age << " ";

    cout << endl;

    // Sort by color
    sort(people.begin(), people.end(), sortByColor);
    for (Person &n : people)
        cout << n.favoriteColor << " ";

    return 0;
}



需要知道的事情

現在,我無法詳盡解釋最後一個示例中發生的所有事情,但我將介紹其中一個函式並解釋它是如何工作的。



按姓名排序函式

1
2
3
4
bool sortByName(const Person &lhs, const Person &rhs) 
{ 
    return lhs.name < rhs.name;
}


這個函式實際上與我們之前建立的函式非常相似,只是我們更改了兩件事。我們將引數型別從 int 改為 Person 型別,並且還稍微更改了返回表示式。

首先,讓我們回顧一下引數的更改。

之所以必須將引數從 int 改為 Person,是因為我們要排序的容器是 vector<Person> 型別。為了能夠呼叫方程 lhs.name < rhs.name,引數必須是 Person 型別,以便我們可以訪問 name 成員。

其次,我們更改了返回方程為 lhs.name < rhs.name。您能猜出這個方程在問什麼嗎?嗯,這基本上是這樣的。請記住,lhs 和 rhs 都指向容器中的一個獨立元素(例如 lhs 是元素 1,rhs 是元素 2)。因此,當我們說 lhs.name 時,我們告訴它訪問元素 1 的物件,然後訪問該物件的 name 成員。rhs.name 也是如此,但它訪問的是元素 2 的物件。因此,當我們要求返回 lhs.name < rhs.name 時,我們問它 lhs 物件的名字是否小於 rhs 物件的名字。

其他函式實際上是相同的,只是使用了結構體的不同成員。



結束 ;p

好了,這就是本教程的全部內容,儘管關於使用 STL 進行排序還有很多需要學習的地方。因此,如果您有興趣,可以在下面查詢一些與 sort() 相關的其他連結。如果您對文章/教程有任何評論(尤其是關於任何錯誤),請告訴我,我喜歡任何型別的反饋,無論好壞。

關於問題也是一樣,如果您不理解任何內容,或者我解釋某個內容的方式不清楚(很有可能 ;p),請透過在此回覆或透過私信告訴我。我很樂意回答您提出的任何問題。

我希望很快能建立更多關於如何使用 STL 演算法的教程。一旦我寫完它們,我將把它們新增到本文中,或者建立一個新的。希望大家喜歡,感謝閱讀,



資源


文件

std::end()
std::begin()
std::sort()
std::stable_sort()
std::greater()
std::less()


資訊

範圍for迴圈
C++11 初始化資訊


~ Zereo