go堆排序需从i=len/2-1倒序建堆,因该索引为最后一个非叶子节点;下标从0开始时left=2i错误,应为2i+1;container/heap与手写堆排序目标不同,api和使用逻辑不可混用。

Go 语言做堆排序,别直接套《算法导论》伪代码——下标从 0 开始,left = 2*i 会崩掉根节点。
为什么 MaxHeapify 从 i := len(slice)/2 - 1 开始?
因为叶子节点不参与建堆,而完全二叉树中,索引在 [len/2, len-1] 范围内的全是叶子。最后一个非叶子节点就是 len/2 - 1(整数除法)。从这里倒着调用 MaxHeapify,才能保证子树已满足堆性质,再向上合并。
- 错写成
i := len(slice)/2:会尝试对第一个叶子节点(甚至越界)调用MaxHeapify,逻辑冗余,可能 panic - 错写成
i := len(slice) - 1:大量无效递归,堆结构彻底错乱 - 建堆时间复杂度本该是
O(n),起始点错就退化成近似O(n log n)
BuildMaxHeap 和 heap.Sort 别混用
container/heap 是动态维护有序性的工具,sort.Slice 或手写堆排序是全量一次性排序。两者目标不同,API 也完全不同。
-
container/heap要求你实现Len、Less、Swap、Push、Pop五个方法;改了元素值不调heap.Fix,堆就“看起来正常但实际废了” -
heapSort函数自己控制heapSize缩减,每次交换后只对前i个元素调MaxHeapify(0),不用删切片、不用重分配内存 - 插入 10 万条数据时,每插一次都
sort.Slice是O(n² log n),而heap.Push累计才O(n log n)
最大堆 vs 最小堆:只改一个符号,但影响整个逻辑流向
Go 标准库的 container/heap 默认按 Less(i,j) 定义顺序——返回 true 表示 i 应该排在 j 前面。所以最大堆不是“把所有比较反过来”,而是让大数优先上浮:
- 最小堆:
func (h IntHeap) Less(i, j int) bool { return h[i] - 最大堆:
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] } - 手写堆排序里,
MaxHeapify的核心是“找最大值下标”,不能只改比较符,左右子节点判断逻辑、交换条件都得同步对齐
最容易被忽略的其实是边界:比如 l := 2*i + 1 后没检查 l 就直接访问 <code>slice[l],运行时 panic 不报具体堆相关错误,只说 index out of range——这时候得回头盯死下标推导和循环终值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











