C++标准库通过std::make_heap和std::sort_heap实现堆排序:先O(n)建最大堆,再O(n log n)转为升序;自定义greater需配套使用,否则行为未定义;不支持稳定排序。

std::make_heap 和 std::sort_heap 是标准做法
C++ 标准库没有叫 heap_sort 的函数,但提供了堆排序所需的两步接口:std::make_heap 构建最大堆,std::sort_heap 将堆转为升序序列。这是最直接、符合 STL 风格的实现方式。
注意:这两函数操作的是随机访问迭代器范围,默认构建**最大堆**,所以最终得到的是**升序排列**(不是降序)。
-
std::make_heap时间复杂度 O(n),不是逐个插入的 O(n log n) - 必须配合
std::sort_heap使用;只调make_heap不会排序,只是堆化 - 原数组会被就地修改,无需额外空间
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
std::make_heap(v.begin(), v.end()); // 建最大堆
std::sort_heap(v.begin(), v.end()); // 排成升序
// v 现在是 {1, 1, 2, 3, 4, 5, 6, 9}
}
自定义比较器时要注意方向
用 std::greater<int>() 可以建最小堆,但此时 std::sort_heap 仍会按“堆顶最小”的逻辑把序列排成**降序**——这点容易误解。
也就是说:sort_heap 总是把当前堆结构“展开”为有序序列,顺序由堆的性质决定,不是固定升序。
立即学习“C++免费学习笔记(深入)”;
- 默认(
less<T>)→ 最大堆 →sort_heap得升序 -
greater<T>→ 最小堆 →sort_heap得降序 - 若想用最小堆但要升序结果,得手动 reverse,不推荐
std::make_heap(v.begin(), v.end(), std::greater<int>()); std::sort_heap(v.begin(), v.end(), std::greater<int>()); // 这样才得升序?错! // 实际上:第二参数必须和第一参数一致,否则行为未定义
别误用 std::push_heap / std::pop_heap 模拟排序
有人试图用 push_heap 逐个建堆、再用 pop_heap 反复取最大值来排序,这虽然可行,但效率低且易出错:
- 每次
push_heap是 O(log n),n 次就是 O(n log n),不如make_heap的 O(n) -
pop_heap只把最大值移到末尾,不自动缩容;需手动调整范围,容易越界或漏元素 - 忘记在
pop_heap后调用v.pop_back()或移动迭代器边界,会导致重复处理或崩溃
典型错误写法:
// ❌ 错误:没更新范围,第二次 pop_heap 作用于整个原区间
for (auto it = v.end(); it != v.begin(); --it) {
std::pop_heap(v.begin(), it); // it 是开区间上界
// 缺少:swap 或 move 操作,且 it 递减逻辑易错
}
数组指针传参时小心迭代器类型
对裸数组使用时,必须用指针作迭代器,且确保类型匹配。例如 int arr[5],std::make_heap(arr, arr + 5) 正确,但写成 std::make_heap(&arr[0], &arr[5]) 虽等价,可读性差;写成 std::make_heap(arr, arr + 6) 就越界了。
- 数组大小必须准确,
make_heap不检查越界 - 如果用
std::array,优先用.begin()/.end(),避免手工算地址 - 对
std::vector,切忌传&v[0]绕过迭代器——虽能编译,但丢失 size 信息,且不适用于空 vector
真正容易被忽略的是:堆操作不保证稳定性,相同元素的相对位置可能改变;如果需要稳定排序,不能用 heap 相关函数。


















