堆(Heap)是一种非常重要的数据结构,在优先队列、堆排序、图算法等领域有着广泛的应用。本文将详细介绍堆的概念、特性以及如何在C、Java和Python中实现堆。
一、堆的基本概念
堆是一种特殊的完全二叉树,它满足以下性质:
-
堆是一棵完全二叉树
-
堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值
堆可以分为两种:
-
最大堆(大顶堆):每个节点的值都大于或等于其子节点的值
-
最小堆(小顶堆):每个节点的值都小于或等于其子节点的值
二、堆的存储方式
由于堆是完全二叉树,我们可以使用数组来高效地存储堆结构。对于数组中的任意一个位置i上的元素:
- 其左子节点在位置:`2i + 1`
- 其右子节点在位置:`2i + 2`
- 其父节点在位置:`(i - 1) / 2`(整数除法)
三、堆的核心操作
堆的核心操作包括:
-
插入元素:将新元素添加到堆的末尾,然后向上调整(heapify up)
-
删除堆顶元素:将堆顶元素与末尾元素交换,删除末尾元素,然后向下调整(heapify down)
-
构建堆:将一个无序数组构建成堆
四、多语言实现
- C语言实现(最大堆)
cpp
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *arr;
int capacity;
int size;
} MaxHeap;
MaxHeap* createHeap(int capacity) {
MaxHeap* heap = (MaxHeap*)malloc(sizeof(MaxHeap));
heap->capacity = capacity;
heap->size = 0;
heap->arr = (int*)malloc(capacity * sizeof(int));
return heap;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void heapifyUp(MaxHeap* heap, int index) {
int parent = (index - 1) / 2;
while (index > 0 && heap->arr[index] > heap->arr[parent]) {
swap(&heap->arr[index], &heap->arr[parent]);
index = parent;
parent = (index - 1) / 2;
}
}
void heapifyDown(MaxHeap* heap, int index) {
int left = 2 * index + 1;
int right = 2 * index + 2;
int largest = index;
if (left < heap->size && heap->arr[left] > heap->arr[largest]) {
largest = left;
}
if (right < heap->size && heap->arr[right] > heap->arr[largest]) {
largest = right;
}
if (largest != index) {
swap(&heap->arr[index], &heap->arr[largest]);
heapifyDown(heap, largest);
}
}
void insert(MaxHeap* heap, int value) {
if (heap->size == heap->capacity) {
printf("Heap is full\n");
return;
}
heap->arr[heap->size] = value;
heapifyUp(heap, heap->size);
heap->size++;
}
int extractMax(MaxHeap* heap) {
if (heap->size == 0) {
printf("Heap is empty\n");
return -1;
}
int max = heap->arr[0];
heap->arr[0] = heap->arr[heap->size - 1];
heap->size--;
heapifyDown(heap, 0);
return max;
}
void buildHeap(MaxHeap* heap, int array[], int n) {
for (int i = 0; i < n; i++) {
insert(heap, array[i]);
}
}
void printHeap(MaxHeap* heap) {
for (int i = 0; i < heap->size; i++) {
printf("%d ", heap->arr[i]);
}
printf("\n");
}
int main() {
MaxHeap* heap = createHeap(10);
int arr[] = {3, 2, 1, 5, 6, 4};
buildHeap(heap, arr, 6);
printf("Max heap: ");
printHeap(heap);
printf("Extracted max: %d\n", extractMax(heap));
printf("Heap after extraction: ");
printHeap(heap);
insert(heap, 7);
printf("Heap after inserting 7: ");
printHeap(heap);
free(heap->arr);
free(heap);
return 0;
}
- Java实现(最小堆)
java
import java.util.Arrays;
public class MinHeap {
private int[] heap;
private int size;
private int capacity;
public MinHeap(int capacity) {
this.capacity = capacity;
this.size = 0;
this.heap = new int[capacity];
}
private int parent(int index) {
return (index - 1) / 2;
}
private int leftChild(int index) {
return 2 * index + 1;
}
private int rightChild(int index) {
return 2 * index + 2;
}
private void swap(int i, int j) {
int temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
private void heapifyUp(int index) {
while (index > 0 && heap[index] < heap[parent(index)]) {
swap(index, parent(index));
index = parent(index);
}
}
private void heapifyDown(int index) {
int minIndex = index;
int left = leftChild(index);
int right = rightChild(index);
if (left < size && heap[left] < heap[minIndex]) {
minIndex = left;
}
if (right < size && heap[right] < heap[minIndex]) {
minIndex = right;
}
if (index != minIndex) {
swap(index, minIndex);
heapifyDown(minIndex);
}
}
public void insert(int value) {
if (size == capacity) {
throw new IllegalStateException("Heap is full");
}
heap[size] = value;
heapifyUp(size);
size++;
}
public int extractMin() {
if (size == 0) {
throw new IllegalStateException("Heap is empty");
}
int min = heap[0];
heap[0] = heap[size - 1];
size--;
heapifyDown(0);
return min;
}
public void buildHeap(int[] array) {
if (array.length > capacity) {
throw new IllegalArgumentException("Array size exceeds heap capacity");
}
System.arraycopy(array, 0, heap, 0, array.length);
size = array.length;
for (int i = size / 2 - 1; i >= 0; i--) {
heapifyDown(i);
}
}
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(heap, size));
}
public static void main(String[] args) {
MinHeap heap = new MinHeap(10);
int[] arr = {3, 2, 1, 5, 6, 4};
heap.buildHeap(arr);
System.out.println("Min heap: " + heap);
System.out.println("Extracted min: " + heap.extractMin());
System.out.println("Heap after extraction: " + heap);
heap.insert(0);
System.out.println("Heap after inserting 0: " + heap);
}
}
- Python实现(最大堆)
python
class MaxHeap:
def __init__(self, capacity):
self.capacity = capacity
self.size = 0
self.heap = [0] * capacity
def parent(self, index):
return (index - 1) // 2
def left_child(self, index):
return 2 * index + 1
def right_child(self, index):
return 2 * index + 2
def swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def heapify_up(self, index):
while index > 0 and self.heap[index] > self.heap[self.parent(index)]:
self.swap(index, self.parent(index))
index = self.parent(index)
def heapify_down(self, index):
max_index = index
left = self.left_child(index)
right = self.right_child(index)
if left < self.size and self.heap[left] > self.heap[max_index]:
max_index = left
if right < self.size and self.heap[right] > self.heap[max_index]:
max_index = right
if index != max_index:
self.swap(index, max_index)
self.heapify_down(max_index)
def insert(self, value):
if self.size == self.capacity:
raise Exception("Heap is full")
self.heap[self.size] = value
self.heapify_up(self.size)
self.size += 1
def extract_max(self):
if self.size == 0:
raise Exception("Heap is empty")
max_val = self.heap[0]
self.heap[0] = self.heap[self.size - 1]
self.size -= 1
self.heapify_down(0)
return max_val
def build_heap(self, array):
if len(array) > self.capacity:
raise Exception("Array size exceeds heap capacity")
self.size = len(array)
self.heap = array.copy()
for i in range(self.size // 2 - 1, -1, -1):
self.heapify_down(i)
def __str__(self):
return str(self.heap[:self.size])
if __name__ == "__main__":
heap = MaxHeap(10)
arr = [3, 2, 1, 5, 6, 4]
heap.build_heap(arr)
print("Max heap:", heap)
print("Extracted max:", heap.extract_max())
print("Heap after extraction:", heap)
heap.insert(7)
print("Heap after inserting 7:", heap)
五、堆的应用场景
-
优先队列:堆是实现优先队列的理想数据结构
-
堆排序:利用堆进行排序,时间复杂度为O(nlogn)
-
图算法:如Dijkstra算法和Prim算法中用于高效获取最小/最大值
-
求Top K问题:维护一个大小为K的堆可以高效解决Top K问题
-
中位数查找:使用两个堆(最大堆和最小堆)可以高效查找中位数
六、堆的时间复杂度分析
-
插入元素(insert):O(log n)
-
删除堆顶元素(extract):O(log n)
-
获取堆顶元素(peek):O(1)
-
构建堆(buildHeap):
-
通过逐个插入:O(n log n)
-
使用Floyd算法:O(n)
七、总结
堆是一种高效的数据结构,特别适合需要频繁访问最大或最小元素的场景。通过本文的C、Java和Python实现,我们可以看到不同语言中堆的实现虽然语法不同,但核心逻辑是一致的。理解堆的原理和实现对于解决许多算法问题非常有帮助。
希望这篇文章能帮助你更好地理解堆数据结构。如果有任何问题,欢迎在评论区留言讨论!