C/C++实践(八)C++ Sort函数详解,从基础到进阶
一、sort
函数基础
1. 函数定义与头文件
sort
函数定义于 <algorithm>
头文件,支持对数组、容器等序列进行高效排序,默认升序排列:
#include <algorithm>
using namespace std;
2. 基本语法
sort(start_iterator, end_iterator [, compare_function]);
- 参数:
start_iterator
:待排序区间的起始迭代器(指向首个元素)end_iterator
:待排序区间的结束迭代器(指向末尾元素的下一位)compare_function
(可选):自定义比较规则
3. 默认排序示例(升序)
int arr[] = {5, 2, 9, 1, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n);
// 结果:{1, 2, 5, 5, 6, 9}
二、降序排序的实现
1. 自定义比较函数
通过返回 a > b
实现降序:
bool compare(int a, int b) {return a > b;
}int arr[] = {5, 2, 9, 1};
sort(arr, arr + 4, compare);
// 结果:{9, 5, 2, 1}
2. 使用 greater<T>
函数对象
无需自定义函数,直接使用标准库模板:
sort(arr, arr + n, greater<int>());
// 结果同上
三、自定义数据类型排序
1. 结构体排序示例
需求:对学生按成绩降序排序,成绩相同则按姓名升序排列。
struct Student {string name;int score;Student(string n, int s) : name(n), score(s) {}
};bool compareStudents(const Student& a, const Student& b) {if (a.score != b.score) return a.score > b.score; // 成绩降序else return a.name < b.name; // 姓名升序
}vector<Student> students = {{"Alice", 90}, {"Bob", 85}, {"Charlie", 90}};
sort(students.begin(), students.end(), compareStudents);
// 排序后顺序:Charlie(90), Alice(90), Bob(85)
2. 重载运算符
通过重载 <
运算符实现默认排序规则:
struct Point {int x, y;Point(int a, int b) : x(a), y(b) {}bool operator<(const Point& other) const {return (x < other.x) || (x == other.x && y < other.y);}
};vector<Point> points = {{3, 4}, {1, 2}, {1, 3}};
sort(points.begin(), points.end());
// 排序后:{1,2}, {1,3}, {3,4}
四、容器排序
1. vector
排序
vector<int> vec = {3, 1, 4, 1, 5};
sort(vec.begin(), vec.end()); // 升序
sort(vec.rbegin(), vec.rend()); // 降序(反向迭代器)
2. string
排序
string s = "algorithm";
sort(s.begin(), s.end()); // "aghilmort"
3. 不支持排序的容器
set
、map
等关联容器基于红黑树自动排序,不可使用sort
。
五、Lambda 表达式与高阶用法
1. Lambda 比较函数
C++11 及以上支持 Lambda 表达式简化代码:
vector<int> nums = {5, 3, 9, 1};
sort(nums.begin(), nums.end(), [](int a, int b) {return a % 3 < b % 3; // 按模3余数升序
});
// 结果:3,9,1,5 → 余数分别为0,0,1,2
2. 捕获外部变量
Lambda 可捕获上下文变量实现动态排序:
int base = 5;
sort(nums.begin(), nums.end(), [base](int a, int b) {return abs(a - base) < abs(b - base); // 按与base的距离升序
});
六、性能分析与底层原理
1. 时间复杂度
sort
平均时间复杂度为 O(N log N),混合使用快速排序、堆排序和插入排序:
- 快速排序:主算法,分区递归。
- 堆排序:避免最坏情况(递归深度过大)。
- 插入排序:处理小规模数据(N ≤ 16)。
2. 稳定性
sort
不稳定(相等元素可能交换顺序)。需稳定排序时使用stable_sort
。
七、进阶应用场景
1. 部分排序(
partial_sort
)仅对前k个元素排序:
vector<int> data = {9, 3, 6, 1, 7}; partial_sort(data.begin(), data.begin() + 3, data.end()); // 前3小元素:1,3,6,其余无序
2. 第N大元素(
nth_element
)快速定位第N大的元素:
vector<int> nums = {5, 2, 9, 1}; nth_element(nums.begin(), nums.begin() + 2, nums.end(), greater<int>()); // 第3大元素:5(排序后位置为索引2)
八、常见问题与调试技巧
1. 无效迭代器
// 危险操作:遍历时删除元素 std::vector<int> data{1,2,3,4,5}; for(auto it = data.begin(); it != data.end(); ++it) { if(*it % 2 == 0) { data.erase(it); // 错误!erase返回新迭代器 } } // 正确方案:利用返回值更新迭代器 for(auto it = data.begin(); it != data.end(); ) { if(*it % 2 == 0) { it = data.erase(it); // C++11起erase返回新迭代器 } else { ++it; } }
失效规律:
vector
/string
:插入导致容量变化时全部失效;删除使被删元素之后失效deque
:头尾插入可能失效,中间插入全失效map
/set
:仅被删元素失效(C++11标准)2. 比较函数规范
- 严格弱序的三重约束
// 错误比较函数:违反非自反性 auto cmp = [](int a, int b) { return a <= b; // a==b时返回true }; std::sort(data.begin(), data.end(), cmp); // 导致未定义行为 // 正确形式:仅使用<关系 auto valid_cmp = [](int a, int b) { return a < b; };
- 状态污染与副作用
struct Item { int value; mutable int compare_count = 0; // 危险! }; auto dangerous_cmp = [](const Item& a, const Item& b) { ++a.compare_count; // 修改对象状态 return a.value < b.value; }; // 可能导致不同排序阶段比较结果不一致
验证技术:
- 使用纯函数属性检查:
static_assert(std::is_invocable_r_v<bool, decltype(cmp), int, int>);
- 单元测试覆盖边界情况:
TEST(ComparatorTest, Reflexivity) { EXPECT_FALSE(cmp(5,5)); } TEST(ComparatorTest, Transitivity) { ASSERT_TRUE(cmp(1,2)); ASSERT_TRUE(cmp(2,3)); EXPECT_TRUE(cmp(1,3)); }
九、综合代码示例
1. 多级排序
struct Employee {string name;int age;double salary;
};vector<Employee> staff = {{"John", 30, 5000}, {"Alice", 25, 6000}, {"Bob", 30, 5500}
};sort(staff.begin(), staff.end(), [](const Employee& a, const Employee& b) {if (a.age != b.age) return a.age < b.age; // 年龄升序else if (a.salary != b.salary) return a.salary > b.salary; // 薪资降序else return a.name < b.name; // 姓名升序
});
2. 混合数据类型排序
vector<pair<int, string>> pairs = {{2, "b"}, {1, "a"}, {2, "a"}};
sort(pairs.begin(), pairs.end()); // 默认按first升序,再按second升序
十、扩展阅读(底层实现模拟)
以下是 sort
函数的简化实现逻辑:
template <typename Iterator, typename Compare>
void my_sort(Iterator first, Iterator last, Compare comp) {if (last - first <= 1) return;// 选择枢纽元并进行分区Iterator pivot = partition(first, last, comp);// 递归排序子区间 my_sort(first, pivot, comp);my_sort(pivot + 1, last, comp);
}
通过上述内容,您可以全面掌握 sort
函数的使用方法与高级技巧。实际开发中建议结合具体场景选择合适的排序策略,并注意性能与稳定性的权衡。