数据结构和算法-堆

Posted zlone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构和算法-堆相关的知识,希望对你有一定的参考价值。

堆的定义

  • 必须是一个完全二叉树(除了最后一层, 每个节点都有两个子节点, 最后一层只能缺少若干个右节点)
  • 堆中每一个节点的值都必须>=(大顶堆)或<=(小顶堆)左右子树节点的值

堆中节点的关系

  • 下标为i节点的父节点序号是i/2
  • 下标为i节点的左子树节点时2*i, 右子树节点是2*i+1

完全二叉树特点:
如果一个完全二叉树有n个节点, 那么从n/2+1个节点开始到n都是叶子节点

构造最小二叉堆

# coding:utf-8

"""
空间复杂度: O(1)  原地排序
时间复杂度: O(nlogn)

不稳定, 因为最后一个节点跟堆顶节点互换可能导致相同元素的顺序互换
"""


class MinHeap(object):
    """
    最小堆
    """

    def __init__(self, nums):
        self.heap_list = [0]  # 填充0位置
        self.size = 0
        self.init_heap(nums)

    def insert(self, data):
        """
        插入元素, 放到最尾部, 上浮
        :param num:
        :return:
        """
        self.heap_list.append(data)
        self.size += 1
        self._go_up(self.size)

    def _go_up(self, i: int):
        """
        末尾元素上浮
        :param i:
        :return:
        """
        while int(i / 2) > 0:
            parent = int(i / 2)
            if self.heap_list[i] < self.heap_list[parent]:
                self.heap_list[i], self.heap_list[parent] = self.heap_list[parent], self.heap_list[i]
            i = parent

    def pop_top(self):
        """
        删除堆顶元素, 使用最后一个值移动到顶部, 再进行下浮
        :return:
        """
        if self.size >= 1:
            top_value = self.heap_list[1]
            self.heap_list[1] = self.heap_list[self.size]
            self.heap_list.pop()
            self.size -= 1
            self._go_down(1)
            return top_value
        else:
            raise Exception("Heap Empty")

    def _go_down(self, i: int):
        while (2 * i) <= self.size:
            min_child_pos = self._get_min_child(i)
            if self.heap_list[i] > self.heap_list[min_child_pos]:
                self.heap_list[i], self.heap_list[min_child_pos] = self.heap_list[min_child_pos], self.heap_list[i]
            i = min_child_pos

    def _get_min_child(self, i: int):
        """
        找出i节点左右子树中较小的节点
        :param i:
        :return:
        """
        left = 2 * i
        right = 2 * i + 1
        if right > self.size:
            return left
        elif self.heap_list[left] < self.heap_list[right]:
            return left
        else:
            return right

    def init_heap(self, nums: list):
        """
        构造堆. 完全二叉树中从 n/2 开始都是叶子节点, 所以只需要让非叶子节点下沉
        :param nums:
        :return:
        """
        start_pos = len(nums) // 2
        self.size = len(nums)

        self.heap_list.extend(nums)
        while start_pos > 0:
            self._go_down(start_pos)
            start_pos -= 1


if __name__ == "__main__":
    nums = [9, 4, 7, 1, 8, 20]

    mh = MinHeap(nums)
    mh.insert(2)
    mh.insert(17)

    res = [mh.pop_top() for _ in range(5)]
    assert res == [1, 2, 4, 7, 8]

应用

优先级队列

  • 高性能定时器
    比如一个定时器中维护了很多的定时任务, 每个任务都设定了一个触发执行的时间点, 定时器每过一个很小的单位时间(比如0.1s), 就会扫描一遍任务, 如果有任务是当前时间, 就触发执行.
    每过0.1s扫描全部任务效率会很低, 所以把所有任务放入一个最小堆中, 堆顶存储的是最先执行任务. 定时器可以根据堆顶任务的执行时间得到一个时间间隔T, 可以直接过T时间后再来检查

  • 爬虫任务的优先队列
    二叉堆常用在爬虫的优先级队列中, 把任务按照优先级放入二叉堆, 调度器可以拿堆顶元素, 保证拿到的是优先级最高的task.

利用堆求Top K

  • 如何在一个包含n个元素的数组中找出前K大数据?
    构建一个K大小的小顶堆, 遍历数组与堆顶元素比较, 如果比堆顶元素大就删除堆顶数据, 把该数据插入堆中, 否则就比较下一个. 最后得到的小顶堆内的K个元素就是前K大的元素.

资料

  • <<数据结构和算法-python>>
  • <<大话数据结构>>
  • <<数据结构和算法-王争>>

技术图片

以上是关于数据结构和算法-堆的主要内容,如果未能解决你的问题,请参考以下文章

算法系列之--Javascript和Kotlin的堆排序算法(原)

数据结构和算法16堆排序

数据结构精要------直接选择和堆排序算法

数据结构与算法-堆排序

挖掘算法中的数据结构:堆排序之 二叉堆(Heapify原地堆排序优化)

Day577.堆排序 -数据结构和算法Java