6-1 堆
堆(Heap)
堆(Heap)是一种基于完全二叉树(Complete Binary Tree)的数据结构,通常用数组来存储。它有两种常见形式:
- 最小堆(Min Heap):每个父节点的值 小于等于 其所有子节点的值,根节点是整个堆中的最小值
- 最大堆(Max Heap):每个父节点的值 大于等于 其所有子节点的值,根节点是整个堆中的最大值
堆的核心用途包括优先队列(Priority Queue)、堆排序(Heap Sort)以及各种 Top-K 问题的求解。
下面展示堆的数组表示与树形结构之间的映射关系。给定数组 [10, 20, 15, 30, 40]:
10 index: 0
/ \ / \
20 15 1 2
/ \ / \
30 40 3 4
索引映射公式(以 0 为起始下标):
- 父节点:
parent(i) = (i - 1) / 2 - 左子节点:
leftChild(i) = 2 * i + 1 - 右子节点:
rightChild(i) = 2 * i + 2
以节点 20(下标 1)为例:父节点是下标 (1-1)/2 = 0(即 10),左子节点是下标 2*1+1 = 3(即 30),右子节点是下标 2*1+2 = 4(即 40)。
堆的性质
堆必须满足以下基本性质:
- 完全二叉树结构:除最后一层外,每一层都被完全填满;最后一层的节点从左到右依次排列,中间不留空位
- 堆序性质:
- 最小堆:父节点的值小于等于所有后代节点的值
- 最大堆:父节点的值大于等于所有后代节点的值
- 数组表示:由于是完全二叉树,可以直接用数组存储,无需额外的指针开销;通过索引公式即可在父子节点之间导航
这意味着堆不像二叉搜索树那样维护全局有序性,它只保证根节点是最值。这一"弱约束"换来了更高的操作效率。
上浮操作(Sift Up / Heapify Up)
上浮操作用于在插入新元素后恢复堆的性质。具体过程:将新元素放在数组末尾,然后不断与父节点比较——如果违反堆性质就交换两者,直到满足条件为止。
以最大堆为例,假设当前堆为 [50, 30, 40, 10, 20, 35],插入元素 45:
Step 0: 插入 45 到末尾
[50, 30, 40, 10, 20, 35, 45]
50
/ \
30 40
/ \ / \
10 20 35 45 <-- 新元素在最后一个位置
Step 1: 45 的父节点是 40(下标 2),45 > 40,交换
[50, 30, 45, 10, 20, 35, 40]
50
/ \
30 45 <-- 45 上浮
/ \ / \
10 20 35 40
Step 2: 45 的父节点是 50(下标 0),45 < 50,停止
堆性质已恢复
#include <iostream>
#include <vector>
using namespace std;
// Sift up: restore max-heap property by moving element at index upward
void siftUp(vector<int>& heap, int index) {
while (index > 0) {
int parent = (index - 1) / 2;
// Max heap: stop if parent is already larger or equal
if (heap[parent] >= heap[index]) {
break;
}
swap(heap[parent], heap[index]);
index = parent;
}
}
int main() {
// Current max-heap
vector<int> heap = {50, 30, 40, 10, 20, 35};
// Insert 45 at the end
heap.push_back(45);
cout << "Before sift up: ";
for (int v : heap) cout << v << " ";
cout << endl;
// Restore heap property
siftUp(heap, heap.size() - 1);
cout << "After sift up: ";
for (int v : heap) cout << v << " ";
cout << endl;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
// Sift up: restore max-heap property by moving element at index upward
void siftUp(int* heap, int size, int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (heap[parent] >= heap[index]) {
break;
}
// Swap parent and child
int temp = heap[parent];
heap[parent] = heap[index];
heap[index] = temp;
index = parent;
}
}
int main() {
int heap[7] = {50, 30, 40, 10, 20, 35, 45};
int size = 7;
printf("Before sift up: ");
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
// Sift up the last element (45)
siftUp(heap, size, size - 1);
printf("After sift up: ");
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
return 0;
}
# Sift up: restore max-heap property by moving element at index upward
def sift_up(heap, index):
while index > 0:
parent = (index - 1) // 2
if heap[parent] >= heap[index]:
break
heap[parent], heap[index] = heap[index], heap[parent]
index = parent
# Current max-heap, then insert 45
heap = [50, 30, 40, 10, 20, 35]
heap.append(45)
print("Before sift up:", heap)
sift_up(heap, len(heap) - 1)
print("After sift up: ", heap)
package main
import "fmt"
// siftUp restores max-heap property by moving element at index upward
func siftUp(heap []int, index int) {
for index > 0 {
parent := (index - 1) / 2
if heap[parent] >= heap[index] {
break
}
heap[parent], heap[index] = heap[index], heap[parent]
index = parent
}
}
func main() {
// Current max-heap, then insert 45
heap := []int{50, 30, 40, 10, 20, 35}
heap = append(heap, 45)
fmt.Print("Before sift up: ")
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
siftUp(heap, len(heap)-1)
fmt.Print("After sift up: ")
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
}
Go 版本使用切片(slice)作为底层数组,通过 append 添加元素,上浮逻辑与 C/Python 版本完全一致。Go 的多重赋值 heap[parent], heap[index] = heap[index], heap[parent] 可以直接完成交换,无需临时变量。
上浮操作从插入位置开始,逐层向上比较并交换,直到父节点大于等于当前节点为止。时间复杂度为 O(log n),因为最多交换树的高度次。
运行该程序将输出:
Before sift up: 50 30 40 10 20 35 45
After sift up: 50 30 45 10 20 35 40
下沉操作(Sift Down / Heapify Down)
下沉操作用于在删除堆顶元素后恢复堆的性质。具体过程:将数组末尾元素移到堆顶,然后不断与较大的子节点比较——如果违反堆性质就交换,直到满足条件为止。
以最大堆为例,假设当前堆为 [50, 30, 45, 10, 20, 35, 40],删除堆顶 50:
Step 0: 删除堆顶 50,将末尾元素 40 移到堆顶
[40, 30, 45, 10, 20, 35]
40 <-- 末尾元素移到堆顶
/ \
30 45
/ \ /
10 20 35
Step 1: 40 的子节点为 30 和 45,较大的是 45,40 < 45,与 45 交换
[45, 30, 40, 10, 20, 35]
45
/ \
30 40 <-- 40 下沉到右子节点
/ \ /
10 20 35
Step 2: 40 的子节点只有 35,40 > 35,停止
堆性质已恢复
#include <iostream>
#include <vector>
using namespace std;
// Sift down: restore max-heap property by moving element at index downward
void siftDown(vector<int>& heap, int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
// Find the largest among node and its children
if (left < size && heap[left] > heap[largest]) {
largest = left;
}
if (right < size && heap[right] > heap[largest]) {
largest = right;
}
// If node is already the largest, heap property is satisfied
if (largest == index) {
break;
}
swap(heap[index], heap[largest]);
index = largest;
}
}
int main() {
// After removing root 50, last element 40 moved to root
vector<int> heap = {40, 30, 45, 10, 20, 35};
int size = heap.size();
cout << "Before sift down: ";
for (int v : heap) cout << v << " ";
cout << endl;
siftDown(heap, 0, size);
cout << "After sift down: ";
for (int v : heap) cout << v << " ";
cout << endl;
return 0;
}
#include <stdio.h>
// Sift down: restore max-heap property by moving element at index downward
void siftDown(int* heap, int index, int size) {
while (1) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) {
largest = left;
}
if (right < size && heap[right] > heap[largest]) {
largest = right;
}
if (largest == index) {
break;
}
int temp = heap[index];
heap[index] = heap[largest];
heap[largest] = temp;
index = largest;
}
}
int main() {
int heap[] = {40, 30, 45, 10, 20, 35};
int size = 6;
printf("Before sift down: ");
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
siftDown(heap, 0, size);
printf("After sift down: ");
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
return 0;
}
# Sift down: restore max-heap property by moving element at index downward
def sift_down(heap, index, size):
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and heap[left] > heap[largest]:
largest = left
if right < size and heap[right] > heap[largest]:
largest = right
if largest == index:
break
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
# After removing root 50, last element 40 moved to root
heap = [40, 30, 45, 10, 20, 35]
size = len(heap)
print("Before sift down:", heap)
sift_down(heap, 0, size)
print("After sift down: ", heap)
package main
import "fmt"
// siftDown restores max-heap property by moving element at index downward
func siftDown(heap []int, index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && heap[left] > heap[largest] {
largest = left
}
if right < size && heap[right] > heap[largest] {
largest = right
}
if largest == index {
break
}
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
}
}
func main() {
// After removing root 50, last element 40 moved to root
heap := []int{40, 30, 45, 10, 20, 35}
size := len(heap)
fmt.Print("Before sift down: ")
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
siftDown(heap, 0, size)
fmt.Print("After sift down: ")
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
}
Go 版本的下沉操作使用无限 for 循环配合 break 跳出,逻辑与其他语言版本一致。切片传参天然支持修改原始数据。
下沉操作从堆顶开始,逐层向下与较大的子节点交换,直到当前节点大于等于所有子节点为止。时间复杂度为 O(log n)。
运行该程序将输出:
Before sift down: 40 30 45 10 20 35
After sift down: 45 30 40 10 20 35
插入操作
插入(Insert)的流程非常直观:先将新元素追加到数组末尾,然后通过上浮操作恢复堆性质。
#include <iostream>
#include <vector>
using namespace std;
void siftUp(vector<int>& heap, int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (heap[parent] >= heap[index]) break;
swap(heap[parent], heap[index]);
index = parent;
}
}
// Insert a value into the max-heap
void heapInsert(vector<int>& heap, int val) {
heap.push_back(val); // Add to end
siftUp(heap, heap.size() - 1); // Restore heap property
}
int main() {
vector<int> heap;
int values[] = {4, 10, 3, 5, 1};
for (int v : values) {
heapInsert(heap, v);
cout << "Insert " << v << " -> ";
for (int x : heap) cout << x << " ";
cout << endl;
}
return 0;
}
#include <stdio.h>
#define MAX_SIZE 100
void siftUp(int* heap, int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (heap[parent] >= heap[index]) break;
int temp = heap[parent];
heap[parent] = heap[index];
heap[index] = temp;
index = parent;
}
}
// Insert a value into the max-heap, return new size
int heapInsert(int* heap, int size, int val) {
heap[size] = val; // Add to end
siftUp(heap, size); // Restore heap property
return size + 1;
}
int main() {
int heap[MAX_SIZE];
int size = 0;
int values[] = {4, 10, 3, 5, 1};
int n = sizeof(values) / sizeof(values[0]);
for (int i = 0; i < n; i++) {
size = heapInsert(heap, size, values[i]);
printf("Insert %d -> ", values[i]);
for (int j = 0; j < size; j++) printf("%d ", heap[j]);
printf("\n");
}
return 0;
}
def sift_up(heap, index):
while index > 0:
parent = (index - 1) // 2
if heap[parent] >= heap[index]:
break
heap[parent], heap[index] = heap[index], heap[parent]
index = parent
def heap_insert(heap, val):
heap.append(val) # Add to end
sift_up(heap, len(heap) - 1) # Restore heap property
heap = []
values = [4, 10, 3, 5, 1]
for v in values:
heap_insert(heap, v)
print(f"Insert {v} -> {heap}")
package main
import "fmt"
func siftUp(heap []int, index int) {
for index > 0 {
parent := (index - 1) / 2
if heap[parent] >= heap[index] {
break
}
heap[parent], heap[index] = heap[index], heap[parent]
index = parent
}
}
// heapInsert inserts a value into the max-heap and returns the updated slice
func heapInsert(heap []int, val int) []int {
heap = append(heap, val) // Add to end
siftUp(heap, len(heap)-1) // Restore heap property
return heap
}
func main() {
var heap []int
values := []int{4, 10, 3, 5, 1}
for _, v := range values {
heap = heapInsert(heap, v)
fmt.Printf("Insert %d -> %v\n", v, heap)
}
}
Go 版本使用切片的 append 函数添加元素,heapInsert 返回更新后的切片。由于 append 可能返回新的底层数组,需要用返回值更新切片变量。
每次插入都先将元素放在数组末尾,然后通过上浮操作逐步将其移动到正确位置。由于完全二叉树的高度为 O(log n),每次插入的时间复杂度为 O(log n)。
运行该程序将输出:
Insert 4 -> [4]
Insert 10 -> [10 4]
Insert 3 -> [10 4 3]
Insert 5 -> [10 5 3 4]
Insert 1 -> [10 5 3 4 1]
提取操作(Extract Min/Max)
提取(Extract)用于移除并返回堆顶的最值元素。具体步骤:先保存堆顶元素,然后将数组末尾元素移到堆顶,缩小数组大小,最后通过下沉操作恢复堆性质。
#include <iostream>
#include <vector>
using namespace std;
void siftDown(vector<int>& heap, int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
swap(heap[index], heap[largest]);
index = largest;
}
}
// Extract and return the maximum element from max-heap
int extractMax(vector<int>& heap) {
if (heap.empty()) {
cerr << "Heap is empty!" << endl;
return -1;
}
int maxVal = heap[0]; // Save root (max)
heap[0] = heap[heap.size() - 1]; // Move last element to root
heap.pop_back(); // Remove last element
if (!heap.empty()) {
siftDown(heap, 0, heap.size()); // Restore heap property
}
return maxVal;
}
int main() {
vector<int> heap = {10, 5, 3, 4, 1};
cout << "Initial heap: ";
for (int v : heap) cout << v << " ";
cout << endl;
// Extract elements one by one
while (!heap.empty()) {
int maxVal = extractMax(heap);
cout << "Extracted: " << maxVal << ", Remaining heap: ";
for (int v : heap) cout << v << " ";
cout << endl;
}
return 0;
}
#include <stdio.h>
void siftDown(int* heap, int index, int size) {
while (1) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
int temp = heap[index];
heap[index] = heap[largest];
heap[largest] = temp;
index = largest;
}
}
// Extract max from heap, return new size via pointer
int extractMax(int* heap, int* size, int* maxVal) {
if (*size == 0) {
printf("Heap is empty!\n");
return 0;
}
*maxVal = heap[0]; // Save root
heap[0] = heap[*size - 1]; // Move last to root
(*size)--; // Shrink
if (*size > 0) {
siftDown(heap, 0, *size);
}
return 1;
}
int main() {
int heap[] = {10, 5, 3, 4, 1};
int size = 5;
printf("Initial heap: ");
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
while (size > 0) {
int maxVal;
extractMax(heap, &size, &maxVal);
printf("Extracted: %d, Remaining heap: ", maxVal);
for (int i = 0; i < size; i++) printf("%d ", heap[i]);
printf("\n");
}
return 0;
}
def sift_down(heap, index, size):
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and heap[left] > heap[largest]:
largest = left
if right < size and heap[right] > heap[largest]:
largest = right
if largest == index:
break
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
def extract_max(heap):
if not heap:
print("Heap is empty!")
return None
max_val = heap[0] # Save root
heap[0] = heap[-1] # Move last to root
heap.pop() # Remove last
if heap:
sift_down(heap, 0, len(heap))
return max_val
heap = [10, 5, 3, 4, 1]
print("Initial heap:", heap)
while heap:
max_val = extract_max(heap)
print(f"Extracted: {max_val}, Remaining heap: {heap}")
package main
import "fmt"
func siftDown(heap []int, index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && heap[left] > heap[largest] {
largest = left
}
if right < size && heap[right] > heap[largest] {
largest = right
}
if largest == index {
break
}
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
}
}
// extractMax removes and returns the maximum element from the max-heap
func extractMax(heap *[]int) (int, bool) {
h := *heap
if len(h) == 0 {
fmt.Println("Heap is empty!")
return 0, false
}
maxVal := h[0]
h[0] = h[len(h)-1]
*heap = h[:len(h)-1]
if len(*heap) > 0 {
siftDown(*heap, 0, len(*heap))
}
return maxVal, true
}
func main() {
heap := []int{10, 5, 3, 4, 1}
fmt.Print("Initial heap: ")
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
for len(heap) > 0 {
maxVal, _ := extractMax(&heap)
fmt.Printf("Extracted: %d, Remaining heap: ", maxVal)
for _, v := range heap {
fmt.Printf("%d ", v)
}
fmt.Println()
}
}
Go 版本使用指针 *[]int 传递切片,以便在函数内修改切片的长度。extractMax 返回提取的值和一个布尔值表示是否成功。
提取操作先保存堆顶元素,用末尾元素替换堆顶后执行下沉。对于最大堆,元素按从大到小的顺序依次被提取出来。时间复杂度为 O(log n)。
运行该程序将输出:
Initial heap: 10 5 3 4 1
Extracted: 10, Remaining heap: 5 4 3 1
Extracted: 5, Remaining heap: 4 1 3
Extracted: 4, Remaining heap: 3 1
Extracted: 3, Remaining heap: 1
Extracted: 1, Remaining heap:
建堆操作(Build Heap)
建堆是将一个无序数组转化为堆的过程。最朴素的方式是逐个插入(时间复杂度 O(n log n)),但更高效的方法是:从最后一个非叶子节点开始,倒序对每个节点执行下沉操作。这种方法的时间复杂度为 O(n)。
给定数组 [4, 10, 3, 5, 1],建最大堆的过程如下:
初始数组: [4, 10, 3, 5, 1]
对应的完全二叉树:
4
/ \
10 3
/ \
5 1
最后一个非叶子节点的下标 = (5/2) - 1 = 1,即节点 10
Step 1: 对下标 1(值 10)执行下沉
子节点为 5 和 1,10 > 5 且 10 > 1,无需交换
数组不变: [4, 10, 3, 5, 1]
Step 2: 对下标 0(值 4)执行下沉
子节点为 10 和 3,较大的是 10,4 < 10,交换
数组变为: [10, 4, 3, 5, 1]
10
/ \
4 3
/ \
5 1
继续下沉:子节点为 5 和 1,较大的是 5,4 < 5,交换
数组变为: [10, 5, 3, 4, 1]
10
/ \
5 3
/ \
4 1
4 是叶子节点,停止。建堆完成。
#include <iostream>
#include <vector>
using namespace std;
void siftDown(vector<int>& heap, int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
swap(heap[index], heap[largest]);
index = largest;
}
}
// Build a max-heap from an unsorted array in O(n)
void buildHeap(vector<int>& heap) {
int n = heap.size();
// Start from last non-leaf node, sift down each
for (int i = n / 2 - 1; i >= 0; i--) {
siftDown(heap, i, n);
}
}
int main() {
vector<int> arr = {4, 10, 3, 5, 1};
cout << "Before build heap: ";
for (int v : arr) cout << v << " ";
cout << endl;
buildHeap(arr);
cout << "After build heap: ";
for (int v : arr) cout << v << " ";
cout << endl;
return 0;
}
#include <stdio.h>
void siftDown(int* heap, int index, int size) {
while (1) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
int temp = heap[index];
heap[index] = heap[largest];
heap[largest] = temp;
index = largest;
}
}
// Build a max-heap from an unsorted array in O(n)
void buildHeap(int* heap, int size) {
for (int i = size / 2 - 1; i >= 0; i--) {
siftDown(heap, i, size);
}
}
int main() {
int arr[] = {4, 10, 3, 5, 1};
int size = sizeof(arr) / sizeof(arr[0]);
printf("Before build heap: ");
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n");
buildHeap(arr, size);
printf("After build heap: ");
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n");
return 0;
}
def sift_down(heap, index, size):
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and heap[left] > heap[largest]:
largest = left
if right < size and heap[right] > heap[largest]:
largest = right
if largest == index:
break
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
def build_heap(heap):
n = len(heap)
# Start from last non-leaf node, sift down each
for i in range(n // 2 - 1, -1, -1):
sift_down(heap, i, n)
arr = [4, 10, 3, 5, 1]
print("Before build heap:", arr)
build_heap(arr)
print("After build heap: ", arr)
package main
import "fmt"
func siftDown(heap []int, index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && heap[left] > heap[largest] {
largest = left
}
if right < size && heap[right] > heap[largest] {
largest = right
}
if largest == index {
break
}
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
}
}
// buildHeap builds a max-heap from an unsorted array in O(n)
func buildHeap(heap []int) {
n := len(heap)
// Start from last non-leaf node, sift down each
for i := n/2 - 1; i >= 0; i-- {
siftDown(heap, i, n)
}
}
func main() {
arr := []int{4, 10, 3, 5, 1}
fmt.Print("Before build heap: ")
for _, v := range arr {
fmt.Printf("%d ", v)
}
fmt.Println()
buildHeap(arr)
fmt.Print("After build heap: ")
for _, v := range arr {
fmt.Printf("%d ", v)
}
fmt.Println()
}
Go 版本的 buildHeap 直接操作传入的切片,无需返回值。从最后一个非叶子节点 n/2 - 1 开始倒序执行下沉。
从最后一个非叶子节点开始倒序执行下沉,是因为叶子节点本身已经满足堆性质(没有子节点)。每次下沉都确保以当前节点为根的子树满足堆性质,自底向上逐步构建整个堆。
运行该程序将输出:
Before build heap: 4 10 3 5 1
After build heap: 10 5 3 4 1
堆排序(Heap Sort)
堆排序利用最大堆的性质实现排序:先建最大堆,然后反复将堆顶(最大值)与末尾元素交换,并缩小堆的范围,对新的堆顶执行下沉。完成后数组即为升序排列。
#include <iostream>
#include <vector>
using namespace std;
void siftDown(vector<int>& heap, int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
swap(heap[index], heap[largest]);
index = largest;
}
}
void buildHeap(vector<int>& heap) {
for (int i = heap.size() / 2 - 1; i >= 0; i--) {
siftDown(heap, i, heap.size());
}
}
// Heap sort: sort array in ascending order using max-heap
void heapSort(vector<int>& arr) {
int n = arr.size();
// Step 1: Build max-heap
buildHeap(arr);
// Step 2: Repeatedly extract max to end
for (int i = n - 1; i > 0; i--) {
swap(arr[0], arr[i]); // Move max to sorted position
siftDown(arr, 0, i); // Restore heap for remaining elements
}
}
int main() {
vector<int> arr = {4, 10, 3, 5, 1};
cout << "Before sort: ";
for (int v : arr) cout << v << " ";
cout << endl;
heapSort(arr);
cout << "After sort: ";
for (int v : arr) cout << v << " ";
cout << endl;
return 0;
}
#include <stdio.h>
void siftDown(int* heap, int index, int size) {
while (1) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
int temp = heap[index];
heap[index] = heap[largest];
heap[largest] = temp;
index = largest;
}
}
void buildHeap(int* heap, int size) {
for (int i = size / 2 - 1; i >= 0; i--) {
siftDown(heap, i, size);
}
}
// Heap sort: sort array in ascending order using max-heap
void heapSort(int* arr, int size) {
buildHeap(arr, size);
for (int i = size - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
siftDown(arr, 0, i);
}
}
int main() {
int arr[] = {4, 10, 3, 5, 1};
int size = sizeof(arr) / sizeof(arr[0]);
printf("Before sort: ");
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n");
heapSort(arr, size);
printf("After sort: ");
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n");
return 0;
}
def sift_down(heap, index, size):
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and heap[left] > heap[largest]:
largest = left
if right < size and heap[right] > heap[largest]:
largest = right
if largest == index:
break
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
def build_heap(heap):
for i in range(len(heap) // 2 - 1, -1, -1):
sift_down(heap, i, len(heap))
def heap_sort(arr):
build_heap(arr)
# Repeatedly extract max to end
for i in range(len(arr) - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
sift_down(arr, 0, i)
arr = [4, 10, 3, 5, 1]
print("Before sort:", arr)
heap_sort(arr)
print("After sort: ", arr)
package main
import "fmt"
func siftDown(heap []int, index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && heap[left] > heap[largest] {
largest = left
}
if right < size && heap[right] > heap[largest] {
largest = right
}
if largest == index {
break
}
heap[index], heap[largest] = heap[largest], heap[index]
index = largest
}
}
func buildHeap(heap []int) {
for i := len(heap)/2 - 1; i >= 0; i-- {
siftDown(heap, i, len(heap))
}
}
// heapSort sorts the array in ascending order using max-heap
func heapSort(arr []int) {
buildHeap(arr)
// Repeatedly extract max to end
for i := len(arr) - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
siftDown(arr, 0, i)
}
}
func main() {
arr := []int{4, 10, 3, 5, 1}
fmt.Print("Before sort: ")
for _, v := range arr {
fmt.Printf("%d ", v)
}
fmt.Println()
heapSort(arr)
fmt.Print("After sort: ")
for _, v := range arr {
fmt.Printf("%d ", v)
}
fmt.Println()
}
Go 版本的堆排序直接在原切片上操作,通过交换堆顶与末尾元素并缩小堆范围来实现原地排序。
堆排序的核心思想是:建最大堆后,每次将堆顶最大值交换到数组末尾,然后对剩余部分重新下沉恢复堆性质。如此反复,数组从后往前逐步有序,最终得到升序序列。
运行该程序将输出:
Before sort: 4 10 3 5 1
After sort: 1 3 4 5 10
完整实现(最大堆)
以下是最大堆的完整实现,包含插入(Insert)、提取最大值(Extract Max)、查看堆顶(Peek)、判空(IsEmpty)、获取大小(Size)、建堆(Build Heap)和堆排序(Heap Sort)。
#include <iostream>
#include <vector>
using namespace std;
class MaxHeap {
private:
vector<int> heap;
// Sift up: move element at index upward to restore heap property
void siftUp(int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (heap[parent] >= heap[index]) break;
swap(heap[parent], heap[index]);
index = parent;
}
}
// Sift down: move element at index downward to restore heap property
void siftDown(int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] > heap[largest]) largest = left;
if (right < size && heap[right] > heap[largest]) largest = right;
if (largest == index) break;
swap(heap[index], heap[largest]);
index = largest;
}
}
public:
// Insert a value into the heap
void insert(int val) {
heap.push_back(val);
siftUp(heap.size() - 1);
}
// Extract and return the maximum element
int extractMax() {
if (heap.empty()) {
cerr << "Heap is empty!" << endl;
return -1;
}
int maxVal = heap[0];
heap[0] = heap.back();
heap.pop_back();
if (!heap.empty()) {
siftDown(0, heap.size());
}
return maxVal;
}
// Return the maximum element without removing it
int peek() const {
if (heap.empty()) {
cerr << "Heap is empty!" << endl;
return -1;
}
return heap[0];
}
// Check if heap is empty
bool isEmpty() const {
return heap.empty();
}
// Return the number of elements
int size() const {
return heap.size();
}
// Build heap from an unsorted array
void buildHeap(vector<int>& arr) {
heap = arr;
for (int i = heap.size() / 2 - 1; i >= 0; i--) {
siftDown(i, heap.size());
}
}
// Heap sort: sort given array in ascending order
void heapSort(vector<int>& arr) {
buildHeap(arr);
for (int i = arr.size() - 1; i > 0; i--) {
swap(arr[0], arr[i]);
siftDown_static(arr, 0, i);
}
}
// Static version for heap sort (operates on external array)
static void siftDown_static(vector<int>& arr, int index, int size) {
while (true) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && arr[left] > arr[largest]) largest = left;
if (right < size && arr[right] > arr[largest]) largest = right;
if (largest == index) break;
swap(arr[index], arr[largest]);
index = largest;
}
}
// Print current heap array
void print() const {
for (int v : heap) cout << v << " ";
cout << endl;
}
};
int main() {
MaxHeap h;
cout << "=== Insert Operations ===" << endl;
int values[] = {4, 10, 3, 5, 1};
for (int v : values) {
h.insert(v);
cout << "Insert " << v << " -> Heap: ";
h.print();
}
cout << "\nHeap size: " << h.size() << endl;
cout << "Heap top (peek): " << h.peek() << endl;
cout << "\n=== Extract Operations ===" << endl;
while (!h.isEmpty()) {
int maxVal = h.extractMax();
cout << "Extracted: " << maxVal << ", Remaining: ";
h.print();
}
cout << "\n=== Build Heap ===" << endl;
vector<int> arr1 = {4, 10, 3, 5, 1};
cout << "Before: ";
for (int v : arr1) cout << v << " ";
cout << endl;
h.buildHeap(arr1);
cout << "After build heap: ";
h.print();
cout << "\n=== Heap Sort ===" << endl;
vector<int> arr2 = {4, 10, 3, 5, 1};
cout << "Before sort: ";
for (int v : arr2) cout << v << " ";
cout << endl;
h.heapSort(arr2);
cout << "After sort: ";
for (int v : arr2) cout << v << " ";
cout << endl;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int size;
} MaxHeap;
// Initialize an empty heap
void initHeap(MaxHeap* h) {
h->size = 0;
}
// Sift up at given index
void siftUp(MaxHeap* h, int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (h->data[parent] >= h->data[index]) break;
int temp = h->data[parent];
h->data[parent] = h->data[index];
h->data[index] = temp;
index = parent;
}
}
// Sift down at given index within given size
void siftDown(MaxHeap* h, int index, int size) {
while (1) {
int largest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && h->data[left] > h->data[largest]) largest = left;
if (right < size && h->data[right] > h->data[largest]) largest = right;
if (largest == index) break;
int temp = h->data[index];
h->data[index] = h->data[largest];
h->data[largest] = temp;
index = largest;
}
}
// Insert a value into the heap
void insert(MaxHeap* h, int val) {
if (h->size >= MAX_SIZE) {
printf("Heap is full!\n");
return;
}
h->data[h->size] = val;
siftUp(h, h->size);
h->size++;
}
// Extract and return the maximum element
int extractMax(MaxHeap* h) {
if (h->size == 0) {
printf("Heap is empty!\n");
return -1;
}
int maxVal = h->data[0];
h->data[0] = h->data[h->size - 1];
h->size--;
if (h->size > 0) {
siftDown(h, 0, h->size);
}
return maxVal;
}
// Return the maximum element without removing it
int peek(MaxHeap* h) {
if (h->size == 0) {
printf("Heap is empty!\n");
return -1;
}
return h->data[0];
}
// Check if heap is empty
int isEmpty(MaxHeap* h) {
return h->size == 0;
}
// Build heap from an unsorted array
void buildHeap(MaxHeap* h, int* arr, int n) {
for (int i = 0; i < n && i < MAX_SIZE; i++) {
h->data[i] = arr[i];
}
h->size = n;
for (int i = n / 2 - 1; i >= 0; i--) {
siftDown(h, i, n);
}
}
// Heap sort: sort array in ascending order
void heapSort(int* arr, int n) {
// Build max-heap in place
MaxHeap h;
for (int i = 0; i < n; i++) {
h.data[i] = arr[i];
}
h.size = n;
for (int i = n / 2 - 1; i >= 0; i--) {
siftDown(&h, i, n);
}
// Extract max repeatedly
for (int i = n - 1; i > 0; i--) {
int temp = h.data[0];
h.data[0] = h.data[i];
h.data[i] = temp;
siftDown(&h, 0, i);
}
// Copy sorted result back
for (int i = 0; i < n; i++) {
arr[i] = h.data[i];
}
}
// Print heap array
void printHeap(MaxHeap* h) {
for (int i = 0; i < h->size; i++) {
printf("%d ", h->data[i]);
}
printf("\n");
}
int main() {
MaxHeap h;
initHeap(&h);
printf("=== Insert Operations ===\n");
int values[] = {4, 10, 3, 5, 1};
int n = sizeof(values) / sizeof(values[0]);
for (int i = 0; i < n; i++) {
insert(&h, values[i]);
printf("Insert %d -> Heap: ", values[i]);
printHeap(&h);
}
printf("\nHeap size: %d\n", h.size);
printf("Heap top (peek): %d\n", peek(&h));
printf("\n=== Extract Operations ===\n");
while (!isEmpty(&h)) {
int maxVal = extractMax(&h);
printf("Extracted: %d, Remaining: ", maxVal);
printHeap(&h);
}
printf("\n=== Build Heap ===\n");
int arr1[] = {4, 10, 3, 5, 1};
printf("Before: ");
for (int i = 0; i < 5; i++) printf("%d ", arr1[i]);
printf("\n");
buildHeap(&h, arr1, 5);
printf("After build heap: ");
printHeap(&h);
printf("\n=== Heap Sort ===\n");
int arr2[] = {4, 10, 3, 5, 1};
printf("Before sort: ");
for (int i = 0; i < 5; i++) printf("%d ", arr2[i]);
printf("\n");
heapSort(arr2, 5);
printf("After sort: ");
for (int i = 0; i < 5; i++) printf("%d ", arr2[i]);
printf("\n");
return 0;
}
class MaxHeap:
def __init__(self):
self.heap = []
def _sift_up(self, index):
"""Move element at index upward to restore heap property."""
while index > 0:
parent = (index - 1) // 2
if self.heap[parent] >= self.heap[index]:
break
self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent]
index = parent
def _sift_down(self, index, size):
"""Move element at index downward to restore heap property."""
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and self.heap[left] > self.heap[largest]:
largest = left
if right < size and self.heap[right] > self.heap[largest]:
largest = right
if largest == index:
break
self.heap[index], self.heap[largest] = self.heap[largest], self.heap[index]
index = largest
def insert(self, val):
"""Insert a value into the heap."""
self.heap.append(val)
self._sift_up(len(self.heap) - 1)
def extract_max(self):
"""Extract and return the maximum element."""
if not self.heap:
print("Heap is empty!")
return None
max_val = self.heap[0]
self.heap[0] = self.heap[-1]
self.heap.pop()
if self.heap:
self._sift_down(0, len(self.heap))
return max_val
def peek(self):
"""Return the maximum element without removing it."""
if not self.heap:
print("Heap is empty!")
return None
return self.heap[0]
def is_empty(self):
"""Check if heap is empty."""
return len(self.heap) == 0
def get_size(self):
"""Return the number of elements."""
return len(self.heap)
def build_heap(self, arr):
"""Build heap from an unsorted array."""
self.heap = arr[:]
n = len(self.heap)
for i in range(n // 2 - 1, -1, -1):
self._sift_down(i, n)
def heap_sort(self):
"""Sort heap elements in ascending order, return sorted list."""
result = self.heap[:]
n = len(result)
# Build max-heap in place
for i in range(n // 2 - 1, -1, -1):
self._sift_down_in(result, i, n)
# Extract max repeatedly
for i in range(n - 1, 0, -1):
result[0], result[i] = result[i], result[0]
self._sift_down_in(result, 0, i)
return result
@staticmethod
def _sift_down_in(arr, index, size):
"""Static sift down for external arrays."""
while True:
largest = index
left = 2 * index + 1
right = 2 * index + 2
if left < size and arr[left] > arr[largest]:
largest = left
if right < size and arr[right] > arr[largest]:
largest = right
if largest == index:
break
arr[index], arr[largest] = arr[largest], arr[index]
index = largest
def __str__(self):
return str(self.heap)
# === Demo ===
h = MaxHeap()
print("=== Insert Operations ===")
for v in [4, 10, 3, 5, 1]:
h.insert(v)
print(f"Insert {v} -> Heap: {h}")
print(f"\nHeap size: {h.get_size()}")
print(f"Heap top (peek): {h.peek()}")
print("\n=== Extract Operations ===")
while not h.is_empty():
max_val = h.extract_max()
print(f"Extracted: {max_val}, Remaining: {h}")
print("\n=== Build Heap ===")
arr1 = [4, 10, 3, 5, 1]
print(f"Before: {arr1}")
h.build_heap(arr1)
print(f"After build heap: {h}")
print("\n=== Heap Sort ===")
arr2 = [4, 10, 3, 5, 1]
h2 = MaxHeap()
h2.build_heap(arr2)
sorted_arr = h2.heap_sort()
print(f"Before sort: {arr2}")
print(f"After sort: {sorted_arr}")
package main
import "fmt"
// MaxHeap represents a max-heap data structure
type MaxHeap struct {
data []int
}
// siftUp moves element at index upward to restore heap property
func (h *MaxHeap) siftUp(index int) {
for index > 0 {
parent := (index - 1) / 2
if h.data[parent] >= h.data[index] {
break
}
h.data[parent], h.data[index] = h.data[index], h.data[parent]
index = parent
}
}
// siftDown moves element at index downward to restore heap property
func (h *MaxHeap) siftDown(index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && h.data[left] > h.data[largest] {
largest = left
}
if right < size && h.data[right] > h.data[largest] {
largest = right
}
if largest == index {
break
}
h.data[index], h.data[largest] = h.data[largest], h.data[index]
index = largest
}
}
// Insert adds a value into the heap
func (h *MaxHeap) Insert(val int) {
h.data = append(h.data, val)
h.siftUp(len(h.data) - 1)
}
// ExtractMax removes and returns the maximum element
func (h *MaxHeap) ExtractMax() (int, bool) {
if len(h.data) == 0 {
fmt.Println("Heap is empty!")
return 0, false
}
maxVal := h.data[0]
h.data[0] = h.data[len(h.data)-1]
h.data = h.data[:len(h.data)-1]
if len(h.data) > 0 {
h.siftDown(0, len(h.data))
}
return maxVal, true
}
// Peek returns the maximum element without removing it
func (h *MaxHeap) Peek() (int, bool) {
if len(h.data) == 0 {
fmt.Println("Heap is empty!")
return 0, false
}
return h.data[0], true
}
// IsEmpty checks if the heap is empty
func (h *MaxHeap) IsEmpty() bool {
return len(h.data) == 0
}
// Size returns the number of elements
func (h *MaxHeap) Size() int {
return len(h.data)
}
// BuildHeap builds a max-heap from an unsorted array
func (h *MaxHeap) BuildHeap(arr []int) {
h.data = make([]int, len(arr))
copy(h.data, arr)
for i := len(h.data)/2 - 1; i >= 0; i-- {
h.siftDown(i, len(h.data))
}
}
// HeapSort sorts the array in ascending order using max-heap
func HeapSort(arr []int) {
n := len(arr)
// Build max-heap in place
for i := n/2 - 1; i >= 0; i-- {
siftDownStatic(arr, i, n)
}
// Repeatedly extract max to end
for i := n - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
siftDownStatic(arr, 0, i)
}
}
// siftDownStatic is a standalone sift down for external arrays
func siftDownStatic(arr []int, index, size int) {
for {
largest := index
left := 2*index + 1
right := 2*index + 2
if left < size && arr[left] > arr[largest] {
largest = left
}
if right < size && arr[right] > arr[largest] {
largest = right
}
if largest == index {
break
}
arr[index], arr[largest] = arr[largest], arr[index]
index = largest
}
}
func (h *MaxHeap) String() string {
return fmt.Sprintf("%v", h.data)
}
func main() {
h := &MaxHeap{}
fmt.Println("=== Insert Operations ===")
values := []int{4, 10, 3, 5, 1}
for _, v := range values {
h.Insert(v)
fmt.Printf("Insert %d -> Heap: %s\n", v, h.String())
}
fmt.Printf("\nHeap size: %d\n", h.Size())
if val, ok := h.Peek(); ok {
fmt.Printf("Heap top (peek): %d\n", val)
}
fmt.Println("\n=== Extract Operations ===")
for !h.IsEmpty() {
maxVal, _ := h.ExtractMax()
fmt.Printf("Extracted: %d, Remaining: %s\n", maxVal, h.String())
}
fmt.Println("\n=== Build Heap ===")
arr1 := []int{4, 10, 3, 5, 1}
fmt.Printf("Before: %v\n", arr1)
h.BuildHeap(arr1)
fmt.Printf("After build heap: %s\n", h.String())
fmt.Println("\n=== Heap Sort ===")
arr2 := []int{4, 10, 3, 5, 1}
fmt.Printf("Before sort: %v\n", arr2)
HeapSort(arr2)
fmt.Printf("After sort: %v\n", arr2)
}
Go 版本使用结构体 MaxHeap 封装切片 []int,通过方法实现各个操作。ExtractMax 和 Peek 返回值加布尔值表示是否成功,这是 Go 的惯用错误处理方式。HeapSort 作为独立函数直接操作外部数组。
以上四个版本分别用 C++ 的类、C 的结构体加函数、Python 的类以及 Go 的结构体实现了最大堆的完整功能。插入 5 个元素后依次提取,元素按降序输出;堆排序则将数组排成升序。
运行该程序将输出:
=== Insert Operations ===
Insert 4 -> Heap: [4]
Insert 10 -> Heap: [10, 4]
Insert 3 -> Heap: [10, 4, 3]
Insert 5 -> Heap: [10, 5, 3, 4]
Insert 1 -> Heap: [10, 5, 3, 4, 1]
Heap size: 5
Heap top (peek): 10
=== Extract Operations ===
Extracted: 10, Remaining: [5, 4, 3, 1]
Extracted: 5, Remaining: [4, 1, 3]
Extracted: 4, Remaining: [3, 1]
Extracted: 3, Remaining: [1]
Extracted: 1, Remaining: []
=== Build Heap ===
Before: [4, 10, 3, 5, 1]
After build heap: [10, 5, 3, 4, 1]
=== Heap Sort ===
Before sort: [4, 10, 3, 5, 1]
After sort: [1, 3, 4, 5, 10]
堆的性质
时间复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 插入(Insert) | O(log n) | 添加到末尾后上浮,最多经过树高次交换 |
| 提取最值(Extract Min/Max) | O(log n) | 末尾元素移到堆顶后下沉,最多经过树高次交换 |
| 查看堆顶(Peek) | O(1) | 直接访问数组首元素 |
| 建堆(Build Heap) | O(n) | 从最后一个非叶子节点倒序下沉,总交换次数为线性 |
| 堆排序(Heap Sort) | O(n log n) | 建堆 O(n) + n 次提取每次 O(log n) |
最小堆与最大堆的选择
- 最小堆:适用于需要快速获取最小值的场景,如 Dijkstra 最短路径算法、合并有序链表、任务调度中优先执行优先级最高的任务
- 最大堆:适用于需要快速获取最大值的场景,如 Top-K 问题(维护最小的 K 个元素用最大堆)、堆排序(排升序用最大堆)
应用场景
- 优先队列(Priority Queue):堆最常见的用途,按优先级而非插入顺序处理元素
- Dijkstra 算法:使用最小堆高效选取当前距离最短的未访问节点
- 堆排序(Heap Sort):原地排序,最坏情况下仍为 O(n log n),但不稳定
- 中位数查找(Median Finding):同时维护一个最大堆(存较小的一半)和一个最小堆(存较大的一半),可在 O(log n) 时间内动态维护中位数
- Top-K 问题:维护大小为 K 的堆,在线性时间内找出前 K 大或前 K 小的元素
标准库支持
- C++:
std::priority_queue,默认为最大堆;可通过自定义比较函数实现最小堆 - Python:
heapq模块,默认实现最小堆;可通过取负值模拟最大堆 - Java:
java.util.PriorityQueue,默认为最小堆
以下是标准库的快速使用示例:
// C++ std::priority_queue (max-heap by default)
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
// Max-heap (default)
priority_queue<int> maxHeap;
maxHeap.push(4);
maxHeap.push(10);
maxHeap.push(3);
maxHeap.push(5);
maxHeap.push(1);
cout << "Max-heap extract: ";
while (!maxHeap.empty()) {
cout << maxHeap.top() << " ";
maxHeap.pop();
}
cout << endl;
// Min-heap (with custom comparator)
priority_queue<int, vector<int>, greater<int>> minHeap;
minHeap.push(4);
minHeap.push(10);
minHeap.push(3);
minHeap.push(5);
minHeap.push(1);
cout << "Min-heap extract: ";
while (!minHeap.empty()) {
cout << minHeap.top() << " ";
minHeap.pop();
}
cout << endl;
return 0;
}
# Python heapq module (min-heap by default)
import heapq
data = [4, 10, 3, 5, 1]
# Min-heap
heapq.heapify(data)
print("Min-heap:", data)
sorted_result = []
while data:
sorted_result.append(heapq.heappop(data))
print("Sorted (ascending):", sorted_result)
# Simulate max-heap using negative values
data2 = [4, 10, 3, 5, 1]
max_data = [-x for x in data2]
heapq.heapify(max_data)
sorted_desc = []
while max_data:
sorted_desc.append(-heapq.heappop(max_data))
print("Sorted (descending):", sorted_desc)
// Go container/heap interface (min-heap by default)
package main
import (
"container/heap"
"fmt"
)
// IntHeap implements heap.Interface for a min-heap of ints
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func main() {
// Min-heap
h := &IntHeap{4, 10, 3, 5, 1}
heap.Init(h)
fmt.Print("Min-heap extract: ")
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
fmt.Println()
// Simulate max-heap using negative values
data := []int{4, 10, 3, 5, 1}
maxH := make(IntHeap, len(data))
for i, v := range data {
maxH[i] = -v
}
heap.Init(&maxH)
fmt.Print("Max-heap extract: ")
for maxH.Len() > 0 {
fmt.Printf("%d ", -heap.Pop(&maxH).(int))
}
fmt.Println()
}
在实际开发中,推荐优先使用标准库提供的堆实现,它们经过充分优化且不易出错。手动实现堆主要用于学习其底层原理或满足特殊定制需求。

浙公网安备 33010602011771号