1 std::sort 算法的概念与用途
std::sort 是 C++ 标准库中的一个通用排序算法,它属于 头文件的一部分。该算法设计得非常通用和灵活,能够对各种类型的序列进行排序,包括数组、向量、列表、甚至自定义容器等。std::sort 的核心在于其内部实现的排序算法,它通常是高效的快速排序、归并排序或者它们的某种组合。
概念
std::sort 的基本概念在于它接受两个迭代器参数,分别表示要排序序列的起始位置和结束位置(不包含结束位置所指向的元素)。这两个迭代器定义了需要排序的范围。算法会根据这个范围,对序列中的元素进行排序。
默认情况下,std::sort 执行的是升序排序,即较小的元素会排在前面,较大的元素会排在后面。然而,std::sort 也支持自定义排序规则,通过提供一个比较函数或可调用对象作为第三个参数,可以实现降序排序或者根据元素的特定属性进行排序。
排序过程
在排序过程中,std::sort 会不断地将序列划分为更小的部分,并对这些部分进行排序,直到整个序列都排好序。具体的排序策略(如快速排序或归并排序)会根据输入数据的大小和特性自动选择,以尽可能减少排序所需的时间。
std::sort 的时间复杂度通常是 O(n log n),其中 n 是要排序的元素数量。这意味着对于大型数据集,std::sort 通常能够提供相当高效的排序性能。
用途
std::sort 在实际编程中用途广泛,几乎任何需要对数据进行排序的场景都可以使用它。以下是一些常见的用途示例:
- 数据预处理:在数据分析或机器学习的任务中,经常需要对数据集进行排序,以便后续处理或分析。
- 查找操作优化:排序后的数据可以更快地执行查找操作,例如二分查找算法就依赖于排序后的序列。
- 数据结构维护:某些数据结构(如二叉搜索树)在插入新元素前需要对已有元素进行排序。
- 可视化与展示:排序数据有助于更直观地展示信息,例如在图表或报告中。
- 算法实现:排序是很多其他算法的基础步骤,例如排序算法本身、图算法、选择算法等。
通过使用 std::sort,开发者可以专注于实现他们的算法逻辑,而无需担心排序算法的具体实现。这样不仅可以提高开发效率,还可以确保排序操作的正确性和性能。
2 std::sort 算法基础
2.1 std::sort 算法的定义与语法
(1)定义
std::sort 是一种通用的排序算法,它基于比较操作对序列中的元素进行排序。该算法不依赖于具体的容器类型,只要提供了合适的迭代器,就可以对任意类型的序列进行排序。std::sort 的实现通常基于高效的排序算法,如快速排序或归并排序,以提供较好的性能。
(2)语法
std::sort 的语法如下:
template< class RandomIt >
void sort( RandomIt first, RandomIt last ); template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
- RandomIt 是一个随机访问迭代器类型,用于指定要排序的序列的开始和结束位置。
- first 和 last 是随机访问迭代器,分别指向要排序序列的起始位置和结束位置的下一个位置。
- Compare 是一个可选的比较函数或可调用对象类型,用于定义排序规则。
- comp 是一个可选的比较函数或可调用对象,用于自定义排序规则。如果不提供 comp,则默认使用 operator< 进行升序排序。
(3)返回值
std::sort 函数没有返回值,它的作用是对指定范围内的元素进行排序。排序完成后,序列中的元素将按照指定的排序规则重新排列。
2.2 std::sort 算法的使用示例
下面是一个使用 std::sort 对整数向量进行升序排序的示例:
#include <iostream>
#include <vector>
#include <algorithm> int main() { std::vector<int> numbers = {5, 2, 8, 1, 9}; // 使用 std::sort 进行升序排序 std::sort(numbers.begin(), numbers.end()); // 输出排序后的结果 for (int num : numbers) { std::cout << num << ' '; } std::cout << std::endl; return 0;
}
输出将是:
1 2 5 8 9
如果要进行降序排序,可以提供一个自定义的比较函数:
#include <iostream>
#include <vector>
#include <algorithm> bool compareDescending(int a, int b) { return a > b;
} int main() { std::vector<int> numbers = {5, 2, 8, 1, 9}; // 使用 std::sort 和自定义比较函数进行降序排序 std::sort(numbers.begin(), numbers.end(), compareDescending); // 输出排序后的结果 for (int num : numbers) { std::cout << num << ' '; } std::cout << std::endl; return 0;
}
输出将是:
9 8 5 2 1
这个降序排序的示例定义了一个名为 compareDescending 的比较函数,它接受两个整数参数并返回 true 如果第一个参数大于第二个参数。然后将这个函数作为第三个参数传递给 std::sort,以便按照降序对向量中的元素进行排序。
3 std::sort 算法的高级用法
3.1 使用 std::greater 降序排序
使用 std::greater 进行降序排序是 C++ 标准库提供的一种简单而直接的方法。std::greater 是一个预定义的函数对象(也称为仿函数),它接受两个参数并返回一个布尔值,指示第一个参数是否大于第二个参数。当与 std::sort 结合使用时,std::greater 可以实现降序排序。
下面是一个使用 std::greater 进行降序排序的示例:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 需要包含这个头文件来使用 std::greater int main() { std::vector<int> numbers = {5, 2, 8, 1, 9}; // 使用 std::sort 和 std::greater 进行降序排序 std::sort(numbers.begin(), numbers.end(), std::greater<int>()); // 输出排序后的结果 for (int num : numbers) { std::cout << num << ' '; } std::cout << std::endl; return 0;
}
这个例子首先创建了一个包含整数的向量 numbers。然后,使用 std::sort 函数对该向量进行排序。std::sort 的第三个参数是 std::greater<int>(),它告诉 std::sort 使用降序排序规则。std::greater<int>() 是一个函数对象,它比较两个 int 类型的值,并返回 true 如果第一个值大于第二个值。
上面代码的输出为:
9 8 5 2 1
需要注意的是,对于基本数据类型(如 int、float、double 等),std::greater 默认已经特化(specialized),因此可以直接传递 std::greater<int>() 而不需要显式提供比较操作。然而,对于自定义类型,则需要提供自定义的比较逻辑或者重载比较操作符(如 operator>)以便与 std::greater 配合使用。
3.2 稳定排序 std::stable_sort 的使用
std::stable_sort 是 C++ 标准库 <algorithm> 头文件中提供的另一个排序算法。与 std::sort 不同,std::stable_sort 在排序时保持了相等元素的相对顺序,即排序是稳定的。这意味着如果原始序列中有两个或更多的相等元素,那么 std::stable_sort 排序后这些元素的相对顺序不会改变。
下面是 std::stable_sort 的详细使用说明:
语法
template< class RandomIt >
void stable_sort( RandomIt first, RandomIt last ); template< class RandomIt, class Compare >
void stable_sort( RandomIt first, RandomIt last, Compare comp );
- RandomIt:随机访问迭代器类型,用于指定要排序序列的开始和结束位置。
- first 和 last:随机访问迭代器,分别指向要排序序列的起始位置和结束位置的下一个位置。
- Compare:可选的比较函数或可调用对象类型,用于定义排序规则。
- comp:可选的比较函数或可调用对象,用于自定义排序规则。如果不提供 comp,则默认使用 operator< 进行升序排序。
使用示例
假设有一个自定义类型 Person,它包含姓名和年龄两个成员变量。现在需要根据年龄对这个类型的对象进行排序,同时保持年龄相同的对象的相对顺序不变。
首先,定义 Person 类型:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string> struct Person { std::string name; int age; Person(const std::string& name, int age) : name(name), age(age) {} // 为了输出方便,重载 << 运算符 friend std::ostream& operator<<(std::ostream& os, const Person& p) { os << p.name << " (" << p.age << ")"; return os; }
};
然后,创建一个 Person 对象的向量,并使用 std::stable_sort 对其进行排序:
int main() { std::vector<Person> people = { {"Alice", 25}, {"Bob", 20}, {"Charlie", 25}, {"David", 30}, {"Eve", 20} }; // 使用 lambda 表达式作为比较函数,根据年龄进行升序排序 std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) { return a.age < b.age; }); // 输出排序后的结果 for (const auto& person : people) { std::cout << person << std::endl; } return 0;
}
在这个例子中,使用了 C++11 的 lambda 表达式来定义比较函数。这个 lambda 表达式接受两个 Person 对象作为参数,并返回 a.age < b.age 的结果,即按照年龄进行升序排序。由于使用了 std::stable_sort,即使 Alice 和 Charlie 的年龄相同,它们在排序后的序列中的相对顺序也会保持不变。
上面代码的输出为:
Bob (20)
Eve (20)
Alice (25)
Charlie (25)
David (30)
注意,Bob 和 Eve 的相对顺序以及 Alice 和 Charlie 的相对顺序在排序后都没有改变。这就是 std::stable_sort 与 std::sort 的主要区别之一。
如果想要按照年龄降序排序,只需调整 lambda 表达式中的比较逻辑即可:
std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) { return a.age > b.age;
});
这样,年龄较大的 Person 对象将会排在前面。
性能考虑
std::stable_sort 的平均时间复杂度通常是 O(n log n),其中 n 是待排序元素的数量。尽管与 std::sort 的时间复杂度相同,但由于 std::stable_sort 需要额外的空间来保持稳定性,并且在某些实现中可能使用了不同的算法,因此它通常会比 std::sort 慢一些。在不需要保持相等元素顺序的场合下,使用 std::sort 会更加高效。
总体而言,std::stable_sort 是一个强大且有用的排序算法,特别是在处理包含重复元素的序列时。通过自定义比较函数或可调用对象,可以很容易地对自定义类型的对象进行排序。
4 std::sort 算法应用与自定义类型
下面是一个 std::sort 算法应用于自定义类型的例子:
首先,定义一个自定义类型,比如一个表示学生的结构体:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string> struct Student { std::string name; int score; Student(const std::string& name, int score) : name(name), score(score) {} // 重载输出运算符以便能方便地打印 Student 对象 friend std::ostream& operator<<(std::ostream& os, const Student& s) { os << s.name << ": " << s.score; return os; }
};
然后,创建一个 Student 对象的向量,并使用 std::sort 对其进行排序。为了排序,需要提供一个比较函数或函数对象。这里提供一个 lambda 表达式作为比较函数:
int main() { std::vector<Student> students = { {"Alice", 90}, {"Bob", 85}, {"Charlie", 92}, {"David", 88}, {"Eve", 90} }; // 使用 lambda 表达式作为比较函数,按分数降序排序 std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) { return a.score > b.score; // 降序排序 }); // 输出排序后的结果 for (const auto& student : students) { std::cout << student << std::endl; } return 0;
}
上面代码的输出为:
Charlie: 92
Alice: 90
Eve: 90
David: 88
Bob: 85
在这个例子中,lambda 表达式 [](const Student& a, const Student& b) { return a.score > b.score; } 被用作比较函数,它比较两个 Student 对象的 score 成员。由于返回 a.score > b.score,这会导致 std::sort 以降序方式对学生进行排序。
如果想以升序排序,只需将比较函数中的 > 改为 < 即可:
std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) { return a.score < b.score; // 升序排序
});