std::ranges::sort
C++20 引入的算法,用于对范围中的元素进行排序。它是传统 std::sort
的范围版本,支持更简洁的语法和更安全的类型检查,同时允许使用投影(Projection)和自定义比较器。
Defined in header | ||
Call signature | ||
template< std::random_access_iterator I, std::sentinel_for<I> S, class Comp = ranges::less, class Proj = std::identity > | (1) | (since C++20) |
template< ranges::random_access_range R, class Comp = ranges::less, class Proj = std::identity > |
参数说明
first
,last
或r
: 要排序的范围的迭代器对或范围对象(如std::vector
)。comp
: 自定义比较器(默认为std::ranges::less
,即升序)。proj
: 投影函数,用于在比较前对元素进行转换(例如按结构体的某个成员排序)。
返回值
- 返回排序后的范围的末尾迭代器(通常可忽略,因为排序直接在原范围上进行)。
示例
示例 1:基本用法(升序排序)
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> vec = {5, 3, 1, 4, 2};
// 默认升序排序
std::ranges::sort(vec);
for (int x : vec) {
std::cout << x << " "; // 输出:1 2 3 4 5
}
}
示例 2:降序排序
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> vec = {5, 3, 1, 4, 2};
// 使用自定义比较器实现降序排序
std::ranges::sort(vec, std::ranges::greater{});
for (int x : vec) {
std::cout << x << " "; // 输出:5 4 3 2 1
}
}
示例 3:自定义比较器和投影
按字符串长度排序:
#include <algorithm>
#include <vector>
#include <ranges>
#include <string>
#include <iostream>
int main() {
std::vector<std::string> words = {"apple", "banana", "cherry", "date"};
// 按字符串长度升序排序
std::ranges::sort(
words,
[](const auto& a, const auto& b) { return a.size() < b.size(); },
[](const std::string& s) { return s.size(); } // 投影到字符串长度
);
for (const auto& word : words) {
std::cout << word << " "; // 输出:date apple banana cherry
}
}
// 输出:
date apple banana cherry
注意事项
-
范围要求:
- 输入范围必须是随机访问范围(如
std::vector
、数组),否则编译失败。 - 不支持单向迭代器(如
std::forward_list
)。
- 输入范围必须是随机访问范围(如
-
排序稳定性:
std::ranges::sort
是不稳定排序,相等元素的相对顺序可能改变。若需稳定排序,使用std::ranges::stable_sort
。
-
复杂度:
- 平均时间复杂度为 O(n log n),最坏情况 O(n²)(取决于具体实现,通常为快速排序的优化版本)。
-
比较器规则:
- 比较器必须满足严格弱序(Strict Weak Ordering),否则行为未定义。
与传统 std::sort
的区别
- 语法更简洁:
- 直接传递范围对象(如
std::ranges::sort(vec)
),无需迭代器对。
- 直接传递范围对象(如
- 支持投影:
- 允许在比较前对元素进行转换(例如按结构体的某个字段排序)。
- 类型安全增强:
- 范围版本通过概念(Concepts)约束参数类型,减少潜在错误。
std::ranges::is_sorted
C++20 引入的算法,用于检查一个范围是否已按指定规则排序。默认使用 ranges::less排序。
Defined in header | ||
Call signature | ||
template< std::forward_iterator I, std::sentinel_for<I> S, class Proj = std::identity, | (1) | (since C++20) |
template< ranges::forward_range R, class Proj = std::identity, std::indirect_strict_weak_order< |
参数说明
first
,last
或r
: 要检查的范围的迭代器对或范围对象。comp
: 自定义比较器(默认为std::ranges::less
,即升序)。proj
: 投影函数,用于在比较前对元素进行转换(例如按结构体的某个成员排序)。
返回值
true
: 范围已按comp
和proj
定义的规则排序。false
: 范围未排序。
示例
示例 1:基本用法(检查升序排序)
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
bool sorted = std::ranges::is_sorted(vec);
std::cout << (sorted ? "Sorted" : "Not sorted"); // 输出:Sorted
}
示例 2:使用投影检查结构体数组
检查结构体数组是否按年龄升序排序:
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>
struct Person {
std::string name;
int age;
};
int main() {
std::vector<Person> people = {
{"Alice", 20},
{"Bob", 25},
{"Charlie", 25},
{"Dave", 30} // 已按 age 升序排序
};
bool sorted = std::ranges::is_sorted(
people,
std::ranges::less{}, // 默认升序比较器
&Person::age // 投影到 age 成员
);
std::cout << "People sorted by age: " << sorted << "\n"; // 输出 1
}
输出:
People sorted by age: 1
注意事项
-
范围要求:
- 输入范围必须是 前向范围(
forward_range
),例如std::vector
、std::list
或数组。 - 不支持单向迭代器(如
std::forward_list
),因为需要多次遍历。
- 输入范围必须是 前向范围(
-
复杂度:
- 时间复杂度为 O(n),需要遍历整个范围。
-
比较器规则:
- 比较器必须满足 严格弱序(Strict Weak Ordering),否则行为未定义。
- 默认比较器为
std::ranges::less
(升序),可替换为std::ranges::greater
(降序)或自定义逻辑。
-
投影函数:
- 投影允许在比较前对元素进行转换(例如提取结构体的某个成员)。
- 若未提供投影,直接比较元素本身。
常见错误
错误 1:未排序的范围
std::vector<int> data = {3, 1, 2};
bool sorted = std::ranges::is_sorted(data); // 返回 false
错误 2:错误使用比较器
// 范围按升序排序,但错误使用降序比较器
std::vector<int> data = {1, 2, 3};
bool sorted = std::ranges::is_sorted(data, std::ranges::greater{}); // 返回 false
错误 3:未投影到正确字段
struct Item { int id; double value; };
std::vector<Item> items = {{1, 3.0}, {2, 2.0}, {3, 1.0}};
// 错误:未投影到 value,实际按 id 排序
bool sorted = std::ranges::is_sorted(items, {}, &Item::id);
// 正确:投影到 value 并按升序检查
bool sorted_correct = std::ranges::is_sorted(items, {}, &Item::value); // 返回 false
std::ranges::is_sorted
是检查范围有序性的高效工具,尤其适合需要结合投影或自定义比较器的场景。它在处理复杂数据类型(如结构体或自定义类)时表现出色,避免了手动编写循环的繁琐。使用时需确保输入范围满足前向迭代要求,并正确实现比较器和投影逻辑。
std::ranges::is_sorted_until
C++20 引入的算法,用于查找范围中第一个破坏排序顺序的元素。它返回一个迭代器,指向最后一个满足排序条件的元素的下一个位置。若整个范围已排序,则返回范围的末尾迭代器。
Defined in header | ||
Call signature | ||
template< std::forward_iterator I, std::sentinel_for<I> S, class Proj = std::identity, | (1) | (since C++20) |
template< std::forward_range R, class Proj = std::identity, std::indirect_strict_weak_order< is_sorted_until( R&& r, Comp comp = {}, Proj proj = {} ); |
参数说明
first
,last
或r
: 要检查的范围的迭代器对或范围对象。comp
: 自定义比较器(默认为std::ranges::less
,即升序)。proj
: 投影函数,用于在比较前对元素进行转换。
返回值
- 返回一个迭代器,指向第一个破坏排序的位置。
- 若整个范围已排序,返回
last
。
示例
示例 1:基本用法
查找第一个破坏升序的位置:
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> data = {1, 3, 5, 4, 6, 7};
auto it = std::ranges::is_sorted_until(data);
if (it != data.end()) {
std::cout << "Order breaks at index: "
<< std::distance(data.begin(), it)
<< ", value: " << *it << "\n";
// 输出:Order breaks at index 3, value: 4
} else {
std::cout << "Entire range is sorted.\n";
}
}
输出:
Order breaks at index: 3, value: 4
注意事项
-
范围要求:
- 输入范围必须是前向范围(
forward_range
),如std::vector
、std::list
或数组。 - 不支持单向迭代器(如
std::forward_list
)。
- 输入范围必须是前向范围(
-
复杂度:
- 时间复杂度为 O(n),需要遍历范围直到找到第一个破坏顺序的元素。
-
与
is_sorted
的区别:is_sorted
返回布尔值表示整个范围是否有序。is_sorted_until
返回迭代器,提供更具体的信息。
std::ranges::stable_sort
C++20 引入的算法,用于对范围进行稳定排序。稳定排序的特点是相等元素的相对顺序在排序后保持不变。
Defined in header | ||
Call signature | ||
template< std::random_access_iterator I, std::sentinel_for<I> S, class Comp = ranges::less, class Proj = std::identity > | (1) | (since C++20) (constexpr since C++26) |
template< ranges::random_access_range R, class Comp = ranges::less, class Proj = std::identity > |
参数说明
r
: 要排序的随机访问范围(如vector
、array
等)。comp
: 自定义比较器(默认为std::ranges::less
)。proj
: 投影函数,用于在比较前转换元素(默认为恒等投影std::identity
)。
返回值
返回指向排序后范围末尾的迭代器(通常是 r.end()
)。
示例
示例 1:自定义比较器 + 投影
对整数向量进行升序排序:
#include <algorithm> // 包含 ranges::stable_sort
#include <vector>
#include <string>
#include <iostream>
#include <functional> // 包含 std::greater
// 定义一个包含需要排序字段的结构体
struct Student {
std::string name;
int age;
int score; // 按此字段降序排序
};
int main() {
// 初始化测试数据(包含相同分数的元素)
std::vector<Student> students = {
{"Alice", 20, 85},
{"Bob", 22, 90},
{"Cathy", 19, 90}, // 与Bob分数相同,但原顺序在后
{"David", 21, 88},
{"Eva", 23, 90} // 与Cathy分数相同,原顺序最后
};
// 打印排序前的顺序
std::cout << "Before stable_sort:\n";
for (const auto& s : students) {
std::cout << s.name << " (Score: " << s.score << ")\n";
}
// 使用 ranges::stable_sort + 自定义比较器 + 投影
std::ranges::stable_sort(students, std::greater{}, &Student::score);
// 打印排序后的结果
std::cout << "\nAfter stable_sort:\n";
for (const auto& s : students) {
std::cout << s.name << " (Score: " << s.score << ")\n";
}
return 0;
}
输出:
Before stable_sort:
Alice (Score: 85)
Bob (Score: 90)
Cathy (Score: 90)
David (Score: 88)
Eva (Score: 90)
After stable_sort:
Bob (Score: 90)
Cathy (Score: 90)
Eva (Score: 90)
David (Score: 88)
Alice (Score: 85)
-
std::greater{}
:自定义比较器,实现降序排列(比较投影后的score
值)。&Student::score
:投影函数,将每个Student
对象映射到其score
成员,使排序基于score
进行。
-
输出结果:
- 排序前,Bob、Cathy、Eva的顺序为Bob → Cathy → Eva。
- 排序后,它们的
score
相同,稳定排序会保持它们的原始相对顺序,因此输出顺序仍为Bob → Cathy → Eva。 - 当
score
相同时,原始顺序(Bob → Cathy → Eva)被保留。
std::ranges::partial_sort
是 C++20 引入的一个算法,用于对范围内的元素进行部分排序。它允许你指定一个范围和一个中间位置,使得该位置之前的元素是整个范围内最小的元素,并且这些元素是有序的,而该位置之后的元素则不一定有序。默认排序使用的是ranges::less。
Defined in header | ||
Call signature | ||
template< std::random_access_iterator I, std::sentinel_for<I> S, class Comp = ranges::less, class Proj = std::identity > | (1) | (since C++20) |
template< ranges::random_access_range R, class Comp = ranges::less, class Proj = std::identity > |
参数说明
-
r
: 要排序的范围,必须是一个随机访问范围(如std::vector
、std::array
等)。 -
middle
: 一个迭代器,指向范围内的某个位置。partial_sort
会使得[begin(r), middle)
范围内的元素是整个范围内最小的元素,并且是有序的。 -
comp
: 比较函数对象,用于定义元素的顺序。默认是std::ranges::less
。 -
proj
: 投影函数对象,用于在比较之前对元素进行转换。默认是std::identity
。
返回值
返回一个迭代器,指向排序后的范围的末尾(即 middle
)。
示例 1
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
int main() {
std::vector<int> v = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
// 对前5个元素进行部分排序
std::ranges::partial_sort(v, v.begin() + 5);
// 输出结果
for (int i : v) {
std::cout << i << " ";
}
return 0;
}
输出:
0 1 2 3 4 8 7 9 6 5
在这个例子中,std::ranges::partial_sort
对 v
的前5个元素进行了排序,使得 v[0]
到 v[4]
是整个向量中最小的5个元素,并且是有序的。剩下的元素 v[5]
到 v[9]
则没有特定的顺序。
示例 2 自定义比较函数
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
int main() {
std::vector<int> v = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
// 对前5个元素进行部分排序,按降序排列
std::ranges::partial_sort(v, v.begin() + 5, std::ranges::greater{});
// 输出结果
for (int i : v) {
std::cout << i << " ";
}
return 0;
}
输出:
9 8 7 6 5 4 2 1 0 3
std::ranges::partial_sort
是一个非常有用的算法,特别适用于当你只需要部分排序结果时。它可以有效地减少排序的开销,尤其是在处理大数据集时。
std::ranges::stable_partition
C++20 引入的一个算法,用于将范围内的元素重新排列,使得满足特定条件的元素排在前面,不满足条件的元素排在后面。与 std::ranges::partition
不同的是,stable_partition
会保持元素的相对顺序。
Defined in header | ||
Call signature | ||
template< std::bidirectional_iterator I, std::sentinel_for<I> S, class Proj = std::identity, | (1) | (since C++20) (constexpr since C++26) |
template< ranges::bidirectional_range R, class Proj = std::identity, std::indirect_unary_predicate< |
参数说明
-
r
: 要重新排列的范围,必须是一个前向范围(如std::vector
、std::list
等)。 -
pred
: 一个谓词函数对象,用于判断元素是否满足条件。满足条件的元素会被放在范围的前半部分。 -
proj
: 投影函数对象,用于在应用谓词之前对元素进行转换。默认是std::identity
。
返回值
返回一个子范围(subrange
),表示不满足条件的元素的起始和结束位置。
示例 1
以下是一个简单的示例,展示如何使用 std::ranges::stable_partition
将一个向量中的偶数放在前面,奇数放在后面,并保持相对顺序。
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 将偶数放在前面,奇数放在后面,并保持相对顺序
auto result = std::ranges::stable_partition(v, [](int i) { return i % 2 == 0; });
// 输出结果
std::cout << "Partitioned vector: ";
for (int i : v) {
std::cout << i << " ";
}
std::cout << "\n";
// 输出不满足条件的元素范围
std::cout << "Unpartitioned elements: ";
for (auto it = result.begin(); it != result.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
return 0;
}
输出:
Partitioned vector: 2 4 6 8 10 1 3 5 7 9 Unpartitioned elements: 1 3 5 7 9
示例 2 自定义投影函数
你可以提供一个自定义的投影函数来对元素进行转换,然后再应用谓词。例如,假设我们有一个结构体,我们想根据结构体的某个成员进行分区:
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
struct Person {
std::string name;
int age;
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 17},
{"Charlie", 30},
{"David", 16},
{"Eve", 22}
};
// 将成年人(age >= 18)放在前面,未成年人放在后面,并保持相对顺序
auto result = std::ranges::stable_partition(people, [](const Person& p) { return p.age >= 18; }, &Person::age);
// 输出结果
std::cout << "Partitioned people: \n";
for (const auto& p : people) {
std::cout << p.name << " (" << p.age << ")\n";
}
// 输出不满足条件的元素范围
std::cout << "Unpartitioned people: \n";
for (auto it = result.begin(); it != result.end(); ++it) {
std::cout << it->name << " (" << it->age << ")\n";
}
return 0;
}
输出:
Partitioned people: Alice (25) Charlie (30) Eve (22) Bob (17) David (16) Unpartitioned people: Bob (17) David (16)