c++如何实现堆排序_c++ heap算法应用详解【进阶】

1次阅读

std::make_heap只构建结构而非全序排列,因堆仅保证父节点不小于子节点且满足完全二叉树性质,故容器仍显“乱序”;需配合std::pop_heap循环将堆顶移至末尾并调整剩余部分才能完成升序排序。

c++如何实现堆排序_c++ heap算法应用详解【进阶】

为什么 std::make_heap 不能直接排序?

很多人调用 std::make_heap 后发现容器没按升序排列,就以为“堆排序失败”——其实这是对堆结构的误解。堆(默认大根堆)只保证 *(begin) ≥ *(begin+1) 且满足完全二叉树父子关系,**不保证整体有序**。真正排序必须配合 std::pop_heap 循环弹出最大值到末尾。

  • std::make_heap 只建堆,时间复杂度 O(n),但数组仍是“堆序”,不是“全序”
  • 要得到升序结果,需重复执行:std::pop_heap(把堆顶换到末尾 + 重排剩余部分),再收缩范围
  • 错误写法:std::make_heap(v.begin(), v.end()); → 此时 v 仍是乱序的(只是满足堆性质)

手写堆排序 vs std::sort_heap:什么时候该自己实现?

std::sort_heap标准库提供的“堆排序收尾操作”,但它**要求输入已是有效堆**。如果你已经用 std::make_heap 建好堆,直接跟 std::sort_heap 就能完成升序排序;但如果需要降序、自定义比较或中间插入/删除,就得手动控制过程。

  • 升序排序(推荐):std::make_heap(v.begin(), v.end()); std::sort_heap(v.begin(), v.end());
  • 降序排序:必须用小根堆,传 std::greater()std::make_heap(v.begin(), v.end(), std::greater());
  • 中途插入元素:用 std::push_heap(先 push_back,再 push_heap
  • 性能注意:std::sort_heap 是 O(n log n),但常数略高于 std::sort;除非明确需要堆的动态特性,否则普通排序优先用 std::sort

std::push_heapstd::pop_heap 的边界陷阱

这两个函数不改变容器大小,只调整逻辑堆范围。最容易出错的是迭代器范围传错,或忘记同步修改容器 size(比如 pop_back)。

  • std::push_heap(v.begin(), v.end()) 要求:最后一个元素是新插入项,且 v.size() > 0
  • std::pop_heap(v.begin(), v.end()) 执行后:最大值被移到 *(v.end()-1),但仍在容器内;必须手动 v.pop_back() 才算真正删除
  • 常见崩溃:std::pop_heap(v.begin(), v.end()); v.pop_back(); 缺一不可,漏掉 pop_back 会导致下次 pop_heap 作用于已无效位置
  • 自定义比较器必须全程一致:建堆、push、pop 都得传同一个 Comp,否则行为未定义

原地堆排序的完整可运行片段(含注释)

#include  #include  #include   int main() {     std::vector v = {3, 1, 4, 1, 5, 9, 2, 6};      // 1. 建大根堆(升序准备)     std::make_heap(v.begin(), v.end());      // 2. 逐个弹出最大值到末尾     for (auto it = v.end(); it != v.begin(); --it) {         std::pop_heap(v.begin(), it); // 把当前堆顶移到 [it-1]     }      // 此时 v 已升序 —— 注意:pop_heap 循环本身完成了排序     for (int x : v) std::cout << x << " "; // 输出:1 1 2 3 4 5 6 9 }

这段代码没调用 std::sort_heap,而是用裸 pop_heap 手动控制每一步。关键点在于循环中 it 是递减的“堆右界”,每次缩小堆范围,最终让所有元素按升序沉淀到前端。这种写法更暴露堆排序本质,也方便在循环中加监控或中断逻辑。

立即学习C++免费学习笔记(深入)”;

真正容易被忽略的,是堆算法对“范围”的严格依赖——所有 xxx_heap 函数都只认你传的迭代器区间,不会检查容器实际 size 或数据一致性。传错一个 end(),结果就不可预测。

text=ZqhQzanResources