什么是堆排序(Heap Sort)?
堆排序是一种基于堆这种特殊结构的选择排序。常见于不允许使用额外空间(in-place)的场景,且性能稳定为 O(n log n)
。
它的过程可以非常简单地概括为四步:
我的理解(堆排序核心步骤)
-
建最大堆(或最小堆)
-
所有数据先变成一个“堆”(最大堆适用于升序)
-
-
交换顶底元素
-
堆顶是最大元素,把它跟堆尾交换,最大元素被“摘出来了”
-
-
干掉底部元素
-
把末尾元素从“堆”里踢出去,相当于缩小堆的尺寸,代表它已经排序完了
-
-
重复前面步骤
-
对剩下的堆重新调整(堆化),最大值又回到堆顶,继续摘
-
一直重复,直到堆中只剩一个元素——排序完成!
图示演示(以最大堆升序为例)
初始数组:[4, 10, 3, 5, 1]
步骤如下:
Step 1: 构建最大堆 [10, 5, 3, 4, 1] Step 2: 交换堆顶与堆尾 → [1, 5, 3, 4, 10] 剔除堆尾(10) → 只对前 4 个元素堆化 Step 3: 堆化 → [5, 4, 3, 1, 10] Step 4: 交换堆顶与堆尾 → [1, 4, 3, 5, 10] 剔除 5 → 堆化前 3 个 ... 最终结果:`[1, 3, 4, 5, 10]`
C# 实现(按步骤清晰注释)
public class HeapSorter { public static void HeapSort(int[] arr) { int n = arr.Length; // 1. 建最大堆 for (int i = n / 2 - 1; i >= 0; i--) Heapify(arr, n, i); // 2. 顶底交换 + 3. 干掉底部元素 + 4. 重复 for (int i = n - 1; i > 0; i--) { // 交换堆顶和堆尾 (arr[0], arr[i]) = (arr[i], arr[0]); // 调整剩余部分为最大堆(i 是新堆的大小) Heapify(arr, i, 0); } } // 调整以 root 为根的子树,使其成为最大堆 private static void Heapify(int[] arr, int heapSize, int root) { int largest = root; int left = 2 * root + 1; int right = 2 * root + 2; if (left < heapSize && arr[left] > arr[largest]) largest = left; if (right < heapSize && arr[right] > arr[largest]) largest = right; if (largest != root) { (arr[root], arr[largest]) = (arr[largest], arr[root]); Heapify(arr, heapSize, largest); } } }
Go 实现(同样思路)
package main import ( "fmt" ) func heapSort(arr []int) { n := len(arr) // 1. 建最大堆 for i := n/2 - 1; i >= 0; i-- { heapify(arr, n, i) } // 2. 顶底交换 + 3. 干掉底部 + 4. 重复 for i := n - 1; i > 0; i-- { arr[0], arr[i] = arr[i], arr[0] // 交换堆顶和堆尾 heapify(arr, i, 0) // 堆化剩下的部分 } } func heapify(arr []int, heapSize int, root int) { largest := root left := 2*root + 1 right := 2*root + 2 if left < heapSize && arr[left] > arr[largest] { largest = left } if right < heapSize && arr[right] > arr[largest] { largest = right } if largest != root { arr[root], arr[largest] = arr[largest], arr[root] heapify(arr, heapSize, largest) } } func main() { arr := []int{4, 10, 3, 5, 1} fmt.Println("原始数组:", arr) heapSort(arr) fmt.Println("排序后:", arr) }
总结一句话:
原创作者: morec 转载于: https://www.cnblogs.com/morec/p/18921958堆排序本质上是一个“摘最大值放到底部 + 重建最大堆”的过程,构造最大堆就是在为这个摘果子流程做准备。
延伸阅读
如果你用的是最小堆,也可以实现降序排序
PriorityQueue<T>
是堆结构的实际应用实时 TopK、图论中的 Dijkstra、调度算法都基于堆
附最小堆使用示例:
using System; using System.Collections.Generic; public class MinHeap<T> where T : IComparable<T> { private List<T> heap = new List<T>(); public int Count => heap.Count; public void Insert(T item) { heap.Add(item); HeapifyUp(Count - 1); } public T ExtractMin() { if (Count == 0) throw new InvalidOperationException("Heap is empty"); T min = heap[0]; heap[0] = heap[Count - 1]; heap.RemoveAt(Count - 1); HeapifyDown(0); return min; } private void HeapifyUp(int index) { while (index > 0) { int parent = (index - 1) / 2; if (heap[index].CompareTo(heap[parent]) >= 0) break; (heap[index], heap[parent]) = (heap[parent], heap[index]); index = parent; } } private void HeapifyDown(int index) { int left, right, smallest; while ((left = 2 * index + 1) < Count) { right = left + 1; smallest = right < Count && heap[right].CompareTo(heap[left]) < 0 ? right : left; if (heap[index].CompareTo(heap[smallest]) <= 0) break; (heap[index], heap[smallest]) = (heap[smallest], heap[index]); index = smallest; } } } // 示例 class Program { static void Main() { MinHeap<int> heap = new MinHeap<int>(); heap.Insert(3); heap.Insert(1); heap.Insert(4); heap.Insert(2); while (heap.Count > 0) { Console.WriteLine(heap.ExtractMin()); // 输出 1, 2, 3, 4 } } }