c#中堆排序需手动实现,因.net无内置heapsort;建堆须从最后一个非叶子节点(索引n/2-1)逆序下沉,升序用大顶堆,交换后需缩小堆范围并重新heapify根节点。

堆排序在 C# 中必须自己实现,.NET 没有内置 HeapSort
不是所有语言都像 Java 那样在 Arrays.sort() 里对基本类型自动用堆排序;C# 的 Array.Sort() 默认用的是 introsort(快排+堆排+插排混合),但你无法直接调用其内部的堆排逻辑。想用纯堆排序,就得手写 BuildHeap 和 Heapify —— 这是绕不开的。
Heapify 下沉操作要从最后一个非叶子节点开始逆序遍历
常见错误是把 Heapify 当成从根节点反复调用,结果建堆失败。正确做法是:先定位最后一个非叶子节点(索引为 (length / 2) - 1),再从该位置倒着往前对每个节点执行下沉。因为叶子节点天然满足堆性质,无需处理。
下沉时注意边界:比较子节点需先检查索引是否越界,左子为 2 * i + 1,右子为 2 * i + 2;若右子存在且大于左子,则选右子做比较对象。
- 数组长度为
n时,最后一个非叶子节点索引是n / 2 - 1(整数除法) - 每次
Heapify后可能触发递归或循环下沉,不能只换一次就停 - 升序排序用大顶堆,降序用小顶堆 —— 别反了
排序循环中交换后要重新 Heapify 根节点,且堆有效长度减一
建完大顶堆后,标准流程是:把堆顶(最大值)和末尾元素交换 → 堆大小减一(即忽略已排好的末尾)→ 对新堆顶执行 Heapify 恢复堆性质。这里容易漏掉两点:一是忘记缩小堆范围,导致把已排序元素又拉回堆里;二是没对新根重新下沉,堆结构迅速失效。
示例关键片段:
for (int i = length - 1; i > 0; i--)
{
Swap(arr, 0, i); // 把最大值移到末尾
Heapify(arr, 0, i); // 注意第三个参数是当前堆长度,不是原数组长
}
泛型版本要注意 IComparable 或传入 Comparison<t></t>
原始数组版只支持 int 等简单类型,实际项目里几乎没人这么用。要支持任意可比类型,得基于 IComparable<t></t> 实现,或更灵活地接受 Comparison<t></t> 委托。后者能兼容自定义排序逻辑(比如按对象某字段),但每次比较都要多一层委托调用开销。
用 Comparison<t></t> 时,Heapify 内部所有大小判断都得改成 comparer(x, y) > 0 形式,不能硬写 >。
别忘了:如果传入 null 比较器,又没实现 IComparable,运行时抛 InvalidOperationException —— 这个异常不明显,调试时容易卡住。










