C++比较器(含堆栈的应用)
为什么会有比较器呢
比较器(Comparator)的存在是为了解决一个核心问题:如何定义元素的顺序关系。
- 灵活性:支持自定义排序规则,对于结构体或类,没有天然的“大小”定义,必须通过比较器指定如何比较。
- 通用性:统一接口适配不同场景,像 sort、priority_queue 这样的模板函数/类需要适用于任意类型,但不同类型可能有不同的比较逻辑。比较器通过函数指针、函数对象或 Lambda 提供统一的接口。
- 数据结构的需求
堆(Heap):priority_queue 需要比较器决定元素的优先级(大根堆或小根堆)。
平衡树(如 std::map):红黑树需要通过比较器维护节点的有序性。
二分查找:lower_bound 依赖有序性,而有序性由比较器定义。
理解C++比较器
C++比较器(comparator)是用于定义元素之间排序规则的函数或函数对象。
基本概念
比较器是一个可调用对象(函数、函数对象或lambda表达式),它接受两个参数并返回一个布尔值,表示第一个参数是否应该排在第二个参数前面。(这很关键,另外前面是指当前位置的左边)
举个例子:return a > b
如果返回 true:表示 a 应该排在 b 前面
如果返回 false:表示 a 不应该排在 b 前面(即 b 应该排在 a 前面或两者相等)
常见使用场景
- 标准库排序算法:如
std::sort
,std::stable_sort
- 关联容器:如
std::set
,std::map
,std::priority_queue
- 二分查找:如
std::lower_bound
,std::upper_bound
比较器规则
比较器需要满足严格弱序关系:
- 反自反性:
comp(a, a)
必须为false - 反对称性:如果
comp(a, b)
为true,则comp(b, a)
必须为false - 传递性:如果
comp(a, b)
和comp(b, c)
都为true,则comp(a, c)
必须为true
示例代码
1. 基本函数比较器
bool compareInt(int a, int b) { |
2. 函数对象(Functor)
struct CompareInt { |
3. Lambda表达式(C++11起)
std::sort(nums.begin(), nums.end(), [](int a, int b) { |
4. 自定义类型排序
struct Person { |
重点来了,注意比较器在堆里的应用,和前面的定义类似,个人理解在堆里第一个元素放在第二个元素的“下边”,而不是“前面”;
C++比较器与大根堆、小根堆的联系
堆的基本概念
完全二叉树是指深度为h的二叉树,除了第h层上,其他所有层的结点树都达到最大值,并且第h层的所有结点连续集中在左边;
堆是一种特殊的完全二叉树,分为两种:
- **大根堆(大顶堆)**:每个父节点的值都大于或等于其子节点的值
- **小根堆(小顶堆)**:每个父节点的值都小于或等于其子节点的值
比较器与堆的关系
在C++中,std::priority_queue
默认实现的是大根堆,但我们可以通过比较器来改变这个行为。
1. 默认大根堆(降序)
// 默认是大根堆,相当于使用 less<int> 比较器 |
这相当于使用比较器:
bool compare(int a, int b) { |
为什么? 因为在堆的实现中,”优先级高”的元素会被放在堆顶。当使用a < b
时,较大的元素会被视为优先级更高。
2. 小根堆(升序)
// 使用 greater<int> 比较器创建小根堆 |
这相当于使用比较器:
bool compare(int a, int b) { |
为什么? 因为当使用a > b
时,较小的元素会被视为优先级更高,从而放在堆顶。
关键理解点
堆的比较器含义:回答”是否应该将a放在b的下方?”
- 返回
true
:a应该放在b的下方(即b的优先级更高) - 返回
false
:a不应该放在b的下方(即a的优先级更高或相等)
- 返回
与排序比较器的区别:
- 排序比较器:
return a > b
→ 降序排序 - 堆比较器:
return a > b
→ 小根堆(因为较大的值会被放在下方)
- 排序比较器:
示例对比
排序比较器(std::sort)
vector<int> nums = {3,1,4,2}; |
堆比较器(priority_queue)
// 小根堆 |
实际应用示例
// 自定义结构体的堆排序 |
这样当priority数字越小,任务优先级越高,会先被执行。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 小林小林爱编程!
评论