Go 语言 Heap 笔记 07 | 青训营

63 阅读2分钟

本篇笔记是笔者在第六届字节跳动青训营期间使用 Go 语言学习堆时所做的笔记

堆的概念

堆是一种十分重要的数据结构,基层数据结构为树。每一个子树的根都比它的字节点大或小。

  • 每个子树的根都比它的节点大的,叫做大顶堆。该树的根节点为整个树节点中最大的值
  • 每个子树的根都比它的节点小的,叫做小顶堆。该树的根节点为整个树节点中最小的值

以下为了方便说明,只讨论大顶堆,小顶堆类似

为什么使用堆

堆借助了树的特点,使得我们可以快速地从某一个集合中得到最大的值。利用该特点,我们可以借助堆实现堆排序,其时间复杂度为 O(nlogn)。本篇笔记不赘述时间复杂度的推导。

下沉,上浮和树状数组

下沉和上浮

在树结构的基础上,结合下沉和上浮的字面意思,我们可以这样理解这两个操作

  • 下沉:将一个节点的大小与它的两个子节点进行比较,若该节点的值小于某个子节点,则将该节点与该节点交换,重复这个操作,直至该节点的值都大于它的两个子节点的值
  • 上浮:将一个节点的大小与它的父节点的值进行比较,若该节点的值比它的父节点值大,则将该节点与它的父节点交换位置,直至该节点的值小于满足子节点的值

下沉和上浮可以理解成,我们在维护堆结构的手段,在具体描述它们的使用情况之前,由于我们常见的堆常常是使用数组实现,因此我们先介绍树状数组的实现

树状数组

树状数组是指使用数组实现树的结构,实质上就是存储的是一个完全二叉树的层次遍历的节点序列,使用索引关系指示节点之间的父子关系,具体地,有以下两种:

  • 数组第一个元素索引为 0: 对于索引 i,若 2 * i + 1 < Size(heap),则 2 * i + 1 是它的左孩子,2 * i + 2 < Size(heap),则 2 * i + 2 是它的右孩子
  • 数组第一个元素索引为 1: 对于索引 i,若 2 * i <= Size(heap),则 2 * i 是它的左孩子,2 * i + 1 <= Size(heap),则 2 * i + 1 是它的右孩子
下沉和上浮应用场景

在考虑应用场景的时候,一定注意,我们是在使用数组实现树结构。

下沉

当我们要弹出当前堆中维护的最大值时,也就是当前数组中的第一个元素,此时我们暂时记录数组中第一个元素的值为maxValue,然后我们将数组中的最后的一个元素的值覆盖到第一个元素的位置,并将数组大小(堆大小)减1即可。此时注意到当前第一个元素并不满足堆的性质,而其又处在整个树的最上部(树根位于顶部),因此我们需要将其进行下沉操作,下沉操作结束后返回maxValue即可

上浮

当我们在建立堆或是向堆中插入元素时,我们会在数组末尾添加一个元素,然后将数组大小(堆大小)加1。此时,我们注意到,新增的元素可能不满足堆的性质,因此我们将其上浮。

reminder:根据上浮和下沉的性质,不难发现其可以使用递归快速地完成

示例

下面是 Leetcode215. 数组中的第K个最大元素的官方GO语言题解,其中体现了下沉的操作:

func findKthLargest(nums []int, k int) int {
    heapSize := len(nums)
    buildMaxHeap(nums, heapSize)
    for i := len(nums) - 1; i >= len(nums) - k + 1; i-- {
        nums[0], nums[i] = nums[i], nums[0]
        heapSize--
        maxHeapify(nums, 0, heapSize)
    }
    return nums[0]
}
​
func buildMaxHeap(a []int, heapSize int) {
    for i := heapSize/2; i >= 0; i-- {
        maxHeapify(a, i, heapSize)
    }
}
​
func maxHeapify(a []int, i, heapSize int) {
    l, r, largest := i * 2 + 1, i * 2 + 2, i
    if l < heapSize && a[l] > a[largest] {
        largest = l
    }
    if r < heapSize && a[r] > a[largest] {
        largest = r
    }
    if largest != i {
        a[i], a[largest] = a[largest], a[i]
        maxHeapify(a, largest, heapSize)
    }
}
​
作者:力扣官方题解
链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/solutions/307351/shu-zu-zhong-de-di-kge-zui-da-yuan-su-by-leetcode-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。