数据结构(4) 堆

堆(Heap)是一种非常重要的数据结构,在优先队列、堆排序、图算法等领域有着广泛的应用。本文将详细介绍堆的概念、特性以及如何在C、Java和Python中实现堆。


一、堆的基本概念

堆是一种特殊的完全二叉树,它满足以下性质:

  1. 堆是一棵完全二叉树

  2. 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值

堆可以分为两种:

  • 最大堆(大顶堆):每个节点的值都大于或等于其子节点的值

  • 最小堆(小顶堆):每个节点的值都小于或等于其子节点的值


二、堆的存储方式

由于堆是完全二叉树,我们可以使用数组来高效地存储堆结构。对于数组中的任意一个位置i上的元素:

  • 其左子节点在位置:`2i + 1`
  • 其右子节点在位置:`2i + 2`
  • 其父节点在位置:`(i - 1) / 2`(整数除法)

三、堆的核心操作

堆的核心操作包括:

  1. 插入元素:将新元素添加到堆的末尾,然后向上调整(heapify up)

  2. 删除堆顶元素:将堆顶元素与末尾元素交换,删除末尾元素,然后向下调整(heapify down)

  3. 构建堆:将一个无序数组构建成堆


四、多语言实现

  1. 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;
}
  1. 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);
    }
}
  1. 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)

五、堆的应用场景

  1. 优先队列:堆是实现优先队列的理想数据结构

  2. 堆排序:利用堆进行排序,时间复杂度为O(nlogn)

  3. 图算法:如Dijkstra算法和Prim算法中用于高效获取最小/最大值

  4. 求Top K问题:维护一个大小为K的堆可以高效解决Top K问题

  5. 中位数查找:使用两个堆(最大堆和最小堆)可以高效查找中位数


六、堆的时间复杂度分析

  • 插入元素(insert):O(log n)

  • 删除堆顶元素(extract):O(log n)

  • 获取堆顶元素(peek):O(1)

  • 构建堆(buildHeap):

  • 通过逐个插入:O(n log n)

  • 使用Floyd算法:O(n)


七、总结

堆是一种高效的数据结构,特别适合需要频繁访问最大或最小元素的场景。通过本文的C、Java和Python实现,我们可以看到不同语言中堆的实现虽然语法不同,但核心逻辑是一致的。理解堆的原理和实现对于解决许多算法问题非常有帮助。

希望这篇文章能帮助你更好地理解堆数据结构。如果有任何问题,欢迎在评论区留言讨论!

相关推荐
万粉变现经纪人12 分钟前
如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘tokenizers’ 问题
python·selenium·测试工具·scrapy·beautifulsoup·fastapi·pip
机器学习之心1 小时前
多目标鲸鱼优化算法(NSWOA),含46种测试函数和9个评价指标,MATLAB实现
算法·matlab·多目标鲸鱼优化算法·46种测试函数·9个评价指标
西阳未落2 小时前
C++基础(21)——内存管理
开发语言·c++·面试
古译汉书2 小时前
嵌入式铁头山羊STM32-各章节详细笔记-查阅传送门
数据结构·笔记·stm32·单片机·嵌入式硬件·个人开发
编程武士2 小时前
从50ms到30ms:YOLOv10部署中图像预处理的性能优化实践
人工智能·python·yolo·性能优化
我的xiaodoujiao2 小时前
Windows系统Web UI自动化测试学习系列2--环境搭建--Python-PyCharm-Selenium
开发语言·python·测试工具
max5006002 小时前
基于Meta Llama的二语习得学习者行为预测计算模型
人工智能·算法·机器学习·分类·数据挖掘·llama
callJJ2 小时前
从 0 开始理解 Spring 的核心思想 —— IoC 和 DI(2)
java·开发语言·后端·spring·ioc·di
超级大福宝2 小时前
使用 LLVM 16.0.4 编译 MiBench 中的 patricia遇到的 rpc 库问题
c语言·c++
wangjialelele2 小时前
Linux中的线程
java·linux·jvm·c++