当前位置: 首页 > news >正文

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. 不支持排序的容器

setmap 等关联容器基于红黑树自动排序,不可使用 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. 比较函数规范
  1. 严格弱序的三重约束
// 错误比较函数:违反非自反性  
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;  
};  

  1. 状态污染与副作用
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 函数的使用方法与高级技巧。实际开发中建议结合具体场景选择合适的排序策略,并注意性能与稳定性的权衡。

相关文章:

  • MySQL MCP 使用案例
  • Mybatis 嵌套子查询动态SQL编写
  • QT6 源(103)篇三:阅读与注释 QPlainTextEdit,给出源代码
  • Mysql、Oracle、Sql Server、达梦之间sql的差异
  • 搭建游戏云服务器的配置要求包括哪些条件?
  • HOW - React NextJS 的同构机制
  • WebRTC中的几个Channel
  • 【网络编程】十、详解 UDP 协议
  • Python教程(五)——模块
  • c/c++的opencv的图像预处理讲解
  • MapReduce Shuffle 全解析:从 Map 端到 Reduce 端的核心数据流​
  • Spring bean 的生命周期、注入方式和作用域
  • C41-为什么要用指针
  • ubuntu环境下 基于Python 打包的 批量命令行可视化操作工具 GUI
  • Vue3项目使用ElDrawer后select方法不生效
  • 分布式 ID 生成的五种方法:优缺点与适用场景
  • 【C++】 —— 笔试刷题day_29
  • 界面控件DevExpress WinForms中文教程:Banded Grid View - API
  • 项目依赖版本修改
  • TensorFlow/Keras实现知识蒸馏案例
  • 国家统计局公布2024年城镇单位就业人员年平均工资情况
  • 师爷、文士、畸人:会稽范啸风及其著述
  • 自然资源部:不动产登记累计化解遗留问题房屋2000多万套
  • 美联储主席:供应冲击或更频繁,将重新评估货币政策方法中的通胀和就业因素
  • 甘肃发布外卖食品安全违法行为典型案例:一商家用鸭肉冒充牛肉被罚
  • 重庆发布经济犯罪案件接报警电子地图,企业可查询导航属地经侦服务点