• 文章
  • 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