数据结构:堆

一、堆的定义

  • 堆是一种完全二叉树 结构,同时满足堆的性质,即父节点和子节点之间存在固定的大小关系。它主要用于高效地获取和维护极值(最大值或最小值),常见的堆有大顶堆和小顶堆两种。
  • 资料:https://pan.quark.cn/s/43d906ddfa1bhttps://pan.quark.cn/s/90ad8fba8347https://pan.quark.cn/s/d9d72152d3cf

二、堆的核心性质

  1. 结构性质

    • 堆是一棵完全二叉树,即除了最后一层,其他层的节点数都达到最大值,且最后一层的节点都靠左排列。
    • 通常用数组来存储堆,对于数组中索引为i的节点(从0开始),其左子节点索引为2i+1,右子节点索引为2i+2,父节点索引为(i-1)//2
  2. 数值性质

    • 大顶堆 :每个父节点的值都大于或等于其左右子节点的值,堆顶(根节点)是整个堆的最大值。
    • 小顶堆 :每个父节点的值都小于或等于其左右子节点的值,堆顶(根节点)是整个堆的最小值。

三、堆的核心操作

1. 上浮(Up-Heapify)

当向堆中插入新元素时,新元素会被放在数组末尾,然后需要通过上浮操作调整其位置,以满足堆的性质。

  • 操作逻辑:比较当前节点与其父节点的值,若不满足堆的性质(大顶堆中当前节点值大于父节点,小顶堆中当前节点值小于父节点),则交换两者位置,重复此过程直到满足堆性质或到达堆顶。

2. 下沉(Down-Heapify)

当堆顶元素被移除或堆中元素值被修改时,需要通过下沉操作调整堆的结构,维持堆的性质。

  • 操作逻辑:比较当前节点与其左右子节点的值,选择符合堆性质的子节点(大顶堆选值最大的子节点,小顶堆选值最小的子节点),若当前节点与该子节点不满足堆性质则交换,重复此过程直到满足堆性质或成为叶子节点。

3. 插入元素

  • 步骤1:将新元素添加到数组末尾;
  • 步骤2:对新元素执行上浮操作,调整堆结构。

4. 删除堆顶元素

  • 步骤1:将堆顶元素与数组末尾元素交换;
  • 步骤2:删除数组末尾的原堆顶元素;
  • 步骤3:对新的堆顶元素执行下沉操作,调整堆结构。

5. 构建堆

将一个无序数组转换为堆结构,通常从最后一个非叶子节点开始,从下到上依次执行下沉操作。

  • 最后一个非叶子节点的索引为(n//2)-1n为数组长度)。

四、堆的时间复杂度

  • 插入操作:时间复杂度为O(log n),上浮操作最多遍历堆的高度次节点。
  • 删除堆顶操作:时间复杂度为O(log n),下沉操作最多遍历堆的高度次节点。
  • 构建堆:时间复杂度为O(n),优于逐个插入的O(n log n)
  • 获取堆顶元素:时间复杂度为O(1),可直接访问数组首个元素。

五、堆的实现示例(小顶堆)

python 复制代码
class MinHeap:
    def __init__(self):
        self.heap = []
    
    def parent(self, idx):
        return (idx - 1) // 2
    
    def left_child(self, idx):
        return 2 * idx + 1
    
    def right_child(self, idx):
        return 2 * idx + 2
    
    def swap(self, i, j):
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
    
    def up_heapify(self, idx):
        # 上浮操作,维护小顶堆性质
        while idx > 0 and self.heap[idx] < self.heap[self.parent(idx)]:
            self.swap(idx, self.parent(idx))
            idx = self.parent(idx)
    
    def down_heapify(self, idx):
        # 下沉操作,维护小顶堆性质
        n = len(self.heap)
        while True:
            smallest = idx
            left = self.left_child(idx)
            right = self.right_child(idx)
            if left < n and self.heap[left] < self.heap[smallest]:
                smallest = left
            if right < n and self.heap[right] < self.heap[smallest]:
                smallest = right
            if smallest == idx:
                break
            self.swap(idx, smallest)
            idx = smallest
    
    def insert(self, val):
        # 插入元素
        self.heap.append(val)
        self.up_heapify(len(self.heap) - 1)
    
    def extract_min(self):
        # 删除并返回堆顶最小值
        if len(self.heap) == 0:
            return None
        if len(self.heap) == 1:
            return self.heap.pop()
        min_val = self.heap[0]
        self.heap[0] = self.heap.pop()
        self.down_heapify(0)
        return min_val
    
    def get_min(self):
        # 获取堆顶最小值
        return self.heap[0] if self.heap else None
    
    def build_heap(self, arr):
        # 构建小顶堆
        self.heap = arr.copy()
        n = len(self.heap)
        # 从最后一个非叶子节点开始下沉
        for i in range((n // 2) - 1, -1, -1):
            self.down_heapify(i)

使用示例

python 复制代码
heap = MinHeap()
# 插入元素
heap.insert(5)
heap.insert(3)
heap.insert(8)
heap.insert(1)
print(heap.get_min())  # 输出1

# 提取堆顶
print(heap.extract_min())  # 输出1
print(heap.get_min())  # 输出3

# 构建堆
arr = [9, 4, 7, 1, 3, 6]
heap.build_heap(arr)
print(heap.get_min())  # 输出1

六、堆的常见应用

  1. 优先队列:堆是优先队列的底层实现,可根据优先级(极值)快速获取和删除元素,例如任务调度、事件驱动系统。
  2. 堆排序 :利用堆的性质实现排序,时间复杂度为O(n log n),分为构建堆和逐次提取堆顶两个阶段。
  3. Top-K问题 :从海量数据中获取前K大或前K小的元素,通过维护一个大小为K的小顶堆(求Top-K大)或大顶堆(求Top-K小),可在O(n log K)时间内完成。
  4. 中位数维护:用大顶堆存储左半部分数据,小顶堆存储右半部分数据,可快速获取中位数。
  5. 多路归并排序:合并多个有序数组时,用堆快速获取各数组当前最小值,实现高效归并。

七、堆的扩展

  1. 二项堆:支持快速合并两个堆,适用于频繁合并的场景。
  2. 斐波那契堆 :理论上部分操作(如合并、插入)的时间复杂度为O(1),但实现复杂,多用于理论分析。
相关推荐
前端小L3 小时前
回溯算法专题(九):棋盘上的巅峰对决——经典「N 皇后」问题
数据结构·算法
橘颂TA4 小时前
【剑斩OFFER】算法的暴力美学——交易逆序对的总数
数据结构·算法·leetcode
2401_841495644 小时前
【LeetCode刷题】合并区间
数据结构·python·算法·leetcode·合并·遍历·排序
xu_yule4 小时前
数据结构(14)二叉树的模拟实现和便利代码
数据结构·算法
UP_Continue5 小时前
哈希表实现--开放定址法
数据结构·哈希算法·散列表
lxh01135 小时前
合并区间题解
数据结构·算法·leetcode
yongui478345 小时前
基于MATLAB的轴承表面织构油膜参数计算程序
数据结构·算法·matlab
立志成为大牛的小牛5 小时前
数据结构——五十七、插入排序(王道408)
数据结构·笔记·程序人生·考研·算法
亭上秋和景清5 小时前
计算器回调函数
c语言·数据结构·算法
Ayanami_Reii5 小时前
详解Splay平衡树
数据结构·算法·线段树·主席树·持久化线段树