数据结构 | 堆

本文简要总结堆的概念。

更新:2023 / 8 / 20


数据结构 | 堆


概念

如果谈到堆排序,那么必然要说说什么是 大根堆 max heap小根堆 min heap [1](#1)

  • max heap
    若根节点存在左右子节点,那么根节点的值大于或等于左右子节点的值;
    是根结点大于或者等于左右子节点的二叉树;
  • min heap
    若根节点存在左右子节点,那么根节点的值小于或等于左右子节点的值;
    是根结点x小于或者等于左右子节点的二叉树;

那么,我们可以总结出关于 max heapmin heap 的结论:

  1. 堆是一颗完全二叉树;
  2. min heap 的根节点是堆中最小值;
    max heap 的根节点是堆中最大值;
  3. 堆适合采用顺序存储;

方法

堆最重要的两个方法就是 插入删除 方法。

插入

将一个元素插入到堆中,使之依然成为一个堆。

步骤

  1. 将元素插入到堆的尾部;
  2. 查看每个子树是否符合大(小)根堆的特点。若不符合则将节点逐层向上调整根和叶子节点的位置,直至从该节点的父节点出发到根节点是一个有序序列,直到所有节点都满足条件,最终依然构成一个堆。

删除

堆在删除元素时,只可以删除根节点,然后使剩下的节点依然成为一个堆。

步骤

  1. 删除根节点,用堆中最后元素进行填充;
  2. 查看每个子树是否符合大(小)根堆的特点。若不符合则进行调整,直至所有节点都满足条件。

示例

堆的构建过程其实就是构建一个符合大根堆或者小根堆的完全二叉树,那么就可以使用顺序表来进行存储。

大根堆

下面举例说明堆的构建过程:

将无序序列 [49, 38, 65, 97, 76, 13, 27, 40] 构建大根堆。

  1. 插入 49

  2. 插入 38

  3. 插入 65

    因为要建立 max heap4965 不符合大根堆的特点,对其进行调整。调整后,如下所示:

  4. 插入 97

    9738 发生冲突,进行调整,

  5. 插入 76

    7665 发生冲突,进行调整,

  6. 插入 13

  7. 插入 27

  8. 插入 40

    4038 发生冲突,进行调整,

    至此,max heap 建立完成。可以看出,根节点是最大值。

插入

在上面的基础上,向 max heap 插入节点 99

  1. 插入 99

    9940 发生冲突,进行调整,


9976 发生冲突,再次进行调整,


99 仍然和 97 存在冲突,再次进行调整,

至此,所有节点都符合堆的特性,99 被成功插入 max heap

删除

在上面的基础上,因为只能删除根节点,所以删除 max heap99

  1. 找到堆中最后的节点 40

    删除根节点 99,根节点被删除后由最后的节点 40 进行补位。

    此时的根节点 40 并不大于或等于左右子节点。因此,寻找此时左右子节点中的最大值 97 与此时的根节点 40 互换以进行调整,

    此时的节点 40 并不大于或等于左子节点 76。因此,寻找此时节点 4076 进行互换,

    至此,所有节点都符合堆的特性,99 被成功从 max heap 删除。

堆排序

堆排序是如何实现的呢?

堆排序其实就是堆的删除过程。每次删除的都是堆的根节点,删除后再进行调整,使得剩下的节点还是堆,然后再删除根节点。重复进行。直至堆中只有一个元素时,便可直接输出。那么删除过程中产生的这个序列即是一个有序序列 [2](#2)

同样以上面的例子为例,来看看堆排序是如何实现的:

  1. 删除 99
    删除 99 的过程可参考上面的 删除 部分的内容。在删除 99 后,剩下的元素构成的堆如下所示:
  2. 删除 97
    剩下的元素构成的堆如下所示:
  3. 删除 76
    剩下的元素构成的堆如下所示:
  4. 删除 65
    剩下的元素构成的堆如下所示:
  5. 删除 49
    剩下的元素构成的堆如下所示:
  6. 删除 40
    剩下的元素构成的堆如下所示:
  7. 删除 38
    剩下的元素构成的堆如下所示:
  8. 删除 27
    剩下的元素构成的堆如下所示:

    至此,堆中仅剩一个元素 13
    所以,最后排序后的序列为 [99, 97, 76, 65, 49, 40, 38, 27, 13],排序完成。

代码实现

Python

大根堆

1.

以无序序列 [49, 38, 65, 97, 76, 13, 27, 40,99] 为例,

python 复制代码
def maxheap(arr, end):
    print('+' * 20 + ' max heapify ')
    height = int((end+1)/ 2 - 1)
    print(f"{'height':20}: {height}")
    for root in range(height, -1, -1):
        while True:
            child = root * 2 + 1
            print(f"{'root':20}: {root}\n{'child':20}: {child}\n{'end':20}: {end}")
            if child > end:
                print(f"-" * 20 + f' child {child} > end {end}, exit loop')
                break
            if child+1 <= end and arr[child] < arr[child+1]:
                print(f"-" * 20 + f' child+1 {child + 1} <= end {end} and arr[child] {arr[child]} < arr[child+1] {arr[child + 1]}')
                child = child + 1
                print(f"{'child now':20}: {child}")
            if arr[root] < arr[child]:
                print(f"-" * 20 + f' arr[root] {arr[root]} < arr[child] {arr[child]}, root {root} child {child}')
                arr[root], arr[child] = arr[child], arr[root]
                root = child
                print(f"-" * 20 + f' arr[root] {arr[root]} > arr[child] {arr[child]}, root {root} child {child} now')
            else:
                print(f"-" * 20 + f' arr[root] {arr[root]} > arr[child] {arr[child]}, root {root} child {child}, exit loop')
                break

    return arr

def sortheap(arr, end):
    for i in range(end, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        maxheap(arr, i-1)

    return arr

if __name__ == '__main__':
    a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
    print(f"排序前:\n{a}")

    print('-' * 100 + ' max heapify ')
    a = maxheap(a, len(a)-1)
    print(f"大根堆:\n{a}")

    print('-' * 100 + ' sort heap ')
    a = sortheap(a, len(a)-1)
    print(f"堆排序:\n{a}")

输出信息如下所示:

python 复制代码
排序前:
[23, 15, 30, 38, 65, 97, 40, 99]
---------------------------------------------------------------------------------------------------- max heapify 
++++++++++++++++++++ max heapify 
height              : 3
root                : 3
child               : 7
end                 : 7
-------------------- arr[root] 38 < arr[child] 99, root 3 child 7
-------------------- arr[root] 38 > arr[child] 38, root 7 child 7 now
root                : 7
child               : 15
end                 : 7
-------------------- child 15 > end 7, exit loop
root                : 2
child               : 5
end                 : 7
-------------------- arr[root] 30 < arr[child] 97, root 2 child 5
-------------------- arr[root] 30 > arr[child] 30, root 5 child 5 now
root                : 5
child               : 11
end                 : 7
-------------------- child 11 > end 7, exit loop
root                : 1
child               : 3
end                 : 7
-------------------- arr[root] 15 < arr[child] 99, root 1 child 3
-------------------- arr[root] 15 > arr[child] 15, root 3 child 3 now
root                : 3
child               : 7
end                 : 7
-------------------- arr[root] 15 < arr[child] 38, root 3 child 7
-------------------- arr[root] 15 > arr[child] 15, root 7 child 7 now
root                : 7
child               : 15
end                 : 7
-------------------- child 15 > end 7, exit loop
root                : 0
child               : 1
end                 : 7
-------------------- arr[root] 23 < arr[child] 99, root 0 child 1
-------------------- arr[root] 23 > arr[child] 23, root 1 child 1 now
root                : 1
child               : 3
end                 : 7
-------------------- child+1 4 <= end 7 and arr[child] 38 < arr[child+1] 65
child now           : 4
-------------------- arr[root] 23 < arr[child] 65, root 1 child 4
-------------------- arr[root] 23 > arr[child] 23, root 4 child 4 now
root                : 4
child               : 9
end                 : 7
-------------------- child 9 > end 7, exit loop
大根堆:
[99, 65, 97, 38, 23, 30, 40, 15]
---------------------------------------------------------------------------------------------------- sort heap 
++++++++++++++++++++ max heapify 
height              : 2
root                : 2
child               : 5
end                 : 6
-------------------- child+1 6 <= end 6 and arr[child] 30 < arr[child+1] 40
child now           : 6
-------------------- arr[root] 97 > arr[child] 40, root 2 child 6, exit loop
root                : 1
child               : 3
end                 : 6
-------------------- arr[root] 65 > arr[child] 38, root 1 child 3, exit loop
root                : 0
child               : 1
end                 : 6
-------------------- child+1 2 <= end 6 and arr[child] 65 < arr[child+1] 97
child now           : 2
-------------------- arr[root] 15 < arr[child] 97, root 0 child 2
-------------------- arr[root] 15 > arr[child] 15, root 2 child 2 now
root                : 2
child               : 5
end                 : 6
-------------------- child+1 6 <= end 6 and arr[child] 30 < arr[child+1] 40
child now           : 6
-------------------- arr[root] 15 < arr[child] 40, root 2 child 6
-------------------- arr[root] 15 > arr[child] 15, root 6 child 6 now
root                : 6
child               : 13
end                 : 6
-------------------- child 13 > end 6, exit loop
++++++++++++++++++++ max heapify 
height              : 2
root                : 2
child               : 5
end                 : 5
-------------------- arr[root] 40 > arr[child] 30, root 2 child 5, exit loop
root                : 1
child               : 3
end                 : 5
-------------------- arr[root] 65 > arr[child] 38, root 1 child 3, exit loop
root                : 0
child               : 1
end                 : 5
-------------------- arr[root] 15 < arr[child] 65, root 0 child 1
-------------------- arr[root] 15 > arr[child] 15, root 1 child 1 now
root                : 1
child               : 3
end                 : 5
-------------------- arr[root] 15 < arr[child] 38, root 1 child 3
-------------------- arr[root] 15 > arr[child] 15, root 3 child 3 now
root                : 3
child               : 7
end                 : 5
-------------------- child 7 > end 5, exit loop
++++++++++++++++++++ max heapify 
height              : 1
root                : 1
child               : 3
end                 : 4
-------------------- child+1 4 <= end 4 and arr[child] 15 < arr[child+1] 23
child now           : 4
-------------------- arr[root] 38 > arr[child] 23, root 1 child 4, exit loop
root                : 0
child               : 1
end                 : 4
-------------------- child+1 2 <= end 4 and arr[child] 38 < arr[child+1] 40
child now           : 2
-------------------- arr[root] 30 < arr[child] 40, root 0 child 2
-------------------- arr[root] 30 > arr[child] 30, root 2 child 2 now
root                : 2
child               : 5
end                 : 4
-------------------- child 5 > end 4, exit loop
++++++++++++++++++++ max heapify 
height              : 1
root                : 1
child               : 3
end                 : 3
-------------------- arr[root] 38 > arr[child] 15, root 1 child 3, exit loop
root                : 0
child               : 1
end                 : 3
-------------------- arr[root] 23 < arr[child] 38, root 0 child 1
-------------------- arr[root] 23 > arr[child] 23, root 1 child 1 now
root                : 1
child               : 3
end                 : 3
-------------------- arr[root] 23 > arr[child] 15, root 1 child 3, exit loop
++++++++++++++++++++ max heapify 
height              : 0
root                : 0
child               : 1
end                 : 2
-------------------- child+1 2 <= end 2 and arr[child] 23 < arr[child+1] 30
child now           : 2
-------------------- arr[root] 15 < arr[child] 30, root 0 child 2
-------------------- arr[root] 15 > arr[child] 15, root 2 child 2 now
root                : 2
child               : 5
end                 : 2
-------------------- child 5 > end 2, exit loop
++++++++++++++++++++ max heapify 
height              : 0
root                : 0
child               : 1
end                 : 1
-------------------- arr[root] 15 < arr[child] 23, root 0 child 1
-------------------- arr[root] 15 > arr[child] 15, root 1 child 1 now
root                : 1
child               : 3
end                 : 1
-------------------- child 3 > end 1, exit loop
++++++++++++++++++++ max heapify 
height              : 0
root                : 0
child               : 1
end                 : 0
-------------------- child 1 > end 0, exit loop
堆排序:
[15, 23, 30, 38, 40, 65, 97, 99]

2. heapq

参考这里 [3](#3)

python 复制代码
import heapq

a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"{'排序前':20}: {a}")

print('-' * 50 + ' min heapify ')
heapq.heapify(a)
print(f"{'小根堆':20}: {a}")

print('-' * 50 + ' max heapify ')
newa = [(-i, a[i]) for i in range(len(a))]
heapq.heapify(newa)
print(f"{'新小根堆':20}: {newa}")
maxheap = list()
while newa:
    _, s = heapq.heappop(newa)
    print(f"_, s: {_}, {s}")
    maxheap.append(s)
print(f"{'大根堆':20}: {maxheap}")

输出信息如下所示:

python 复制代码
排序前                 : [49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify 
小根堆                 : [13, 38, 27, 40, 76, 65, 49, 97, 99]
-------------------------------------------------- max heapify 
新小根堆                : [(-8, 99), (-7, 97), (-6, 49), (-3, 40), (-4, 76), (-5, 65), (-2, 27), (-1, 38), (0, 13)]
_, s: -8, 99
_, s: -7, 97
_, s: -6, 49
_, s: -5, 65
_, s: -4, 76
_, s: -3, 40
_, s: -2, 27
_, s: -1, 38
_, s: 0, 13
大根堆                 : [99, 97, 49, 65, 76, 40, 27, 38, 13]

小根堆

1.

以无序序列 [49, 38, 65, 97, 76, 13, 27, 40,99] 为例,

python 复制代码
def minheap(arr, start, end):
    height = int((end+1)/ 2 - 1)
    for root in range(height, -1, -1):
        while True:
            child = root * 2 + 1
            if child > end:
                break
            if child-1 >= start and arr[child] > arr[child-1]:
                child = child-1
            if arr[root] > arr[child]:
                arr[root], arr[child] = arr[child], arr[root]
                root = child
            else:
                break
    return arr

def sortheap(arr, start, end):
    for i in range(end, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        minheap(arr, 0, i-1)

    return arr

if __name__ == '__main__':
    a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
    print(f"排序前:\n{a}")

    print('-' * 50 + ' min heapify ')
    a = minheap(a, 0, len(a)-1)
    print(f"小根堆:\n{a}")

    print('-' * 50 + ' sort heap ')
    arr = sortheap(a, 0, len(a)-1)
    print(f"堆排序:\n{a}")

输出信息如下所示:

python 复制代码
排序前:
[49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify 
小根堆:
[13, 27, 38, 40, 76, 65, 97, 49, 99]
-------------------------------------------------- sort heap 
堆排序:
[99, 97, 76, 65, 49, 40, 38, 27, 13]

2. heapq

参考这里 [3](#3)

python 复制代码
import heapq

a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"{'排序前':20}: {a}")

print('-' * 50 + ' min heapify ')
heapq.heapify(a)
print(f"{'小根堆':20}: {a}")

print('-' * 50 + ' min heapify ')
newa = [(i, a[i]) for i in range(len(a))]
heapq.heapify(newa)
print(f"{'新小根堆':20}: {newa}")
minheap = list()
while newa:
    _, s = heapq.heappop(newa)
    print(f"_, s: {_}, {s}")
    minheap.append(s)
print(f"{'小根堆':20}: {minheap}")

输出信息如下所示:

python 复制代码
排序前                 : [49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify 
小根堆                 : [13, 38, 27, 40, 76, 65, 49, 97, 99]
-------------------------------------------------- min heapify 
新小根堆                : [(0, 13), (1, 38), (2, 27), (3, 40), (4, 76), (5, 65), (6, 49), (7, 97), (8, 99)]
_, s: 0, 13
_, s: 1, 38
_, s: 2, 27
_, s: 3, 40
_, s: 4, 76
_, s: 5, 65
_, s: 6, 49
_, s: 7, 97
_, s: 8, 99
小根堆                 : [13, 38, 27, 40, 76, 65, 49, 97, 99]

参考链接

#todo:
关于 Python 标准库 heapq 的 大根堆/最大堆的 API~


  1. 浅谈大根堆,小根堆,以及堆排序(python)实现 ↩︎

  2. Python 堆排序 ↩︎

  3. python用heapq模块构建大根堆 ↩︎ ↩︎

相关推荐
努力写代码的熊大26 分钟前
随机链表的复制数据结构oj题(力口138)
数据结构·链表
Am心若依旧4091 小时前
C++设计模式之创建型模式
java·开发语言·数据结构·c++·设计模式
手握风云-2 小时前
Java数据结构第二十五期:红黑树传奇,当二叉树穿上 “红黑铠甲” 应对失衡挑战
数据结构
努力努力再努力wz2 小时前
【c++深入系列】:万字详解list(附模拟实现的list源码)
运维·c语言·开发语言·数据结构·c++·list
减瓦2 小时前
极致简单的哈希表
java·数据结构
仟濹3 小时前
【数据结构】「栈」(顺序栈、共享栈、链栈)
c语言·数据结构·算法
凤年徐3 小时前
【数据结构】栈和队列-----数据结构中的双生花
c语言·开发语言·数据结构·c++·笔记·算法·链表
艾莉丝努力练剑13 小时前
【数据结构与算法】数据结构初阶:详解顺序表和链表(四)——单链表(下)
c语言·开发语言·数据结构·学习·算法·链表
笑衬人心。15 小时前
Hashtable 与 HashMap 的区别笔记
java·数据结构·笔记
秋说15 小时前
【PTA数据结构 | C语言版】根据层序序列重构二叉树
c语言·数据结构·算法