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)。


堆的性质

堆必须满足以下基本性质:

  1. 完全二叉树结构:除最后一层外,每一层都被完全填满;最后一层的节点从左到右依次排列,中间不留空位
  2. 堆序性质
    • 最小堆:父节点的值小于等于所有后代节点的值
    • 最大堆:父节点的值大于等于所有后代节点的值
  3. 数组表示:由于是完全二叉树,可以直接用数组存储,无需额外的指针开销;通过索引公式即可在父子节点之间导航

这意味着堆不像二叉搜索树那样维护全局有序性,它只保证根节点是最值。这一"弱约束"换来了更高的操作效率。


上浮操作(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,通过方法实现各个操作。ExtractMaxPeek 返回值加布尔值表示是否成功,这是 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,默认为最大堆;可通过自定义比较函数实现最小堆
  • Pythonheapq 模块,默认实现最小堆;可通过取负值模拟最大堆
  • Javajava.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()
}

在实际开发中,推荐优先使用标准库提供的堆实现,它们经过充分优化且不易出错。手动实现堆主要用于学习其底层原理或满足特殊定制需求。

posted @ 2026-04-17 00:07  游翔  阅读(6)  评论(0)    收藏  举报