数据结构和算法-堆

堆的定义

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

堆中节点的关系

  • 下标为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大的元素.

资料

  • <>
  • <>
  • <>

原文地址:https://www.cnblogs.com/zlone/p/11043158.html

时间: 2024-10-17 01:16:06

数据结构和算法-堆的相关文章

(数据结构与算法) 堆

1.堆的定义和分类 ==================================================================================================================== 2.堆-存储结构(数组) ==============================================================================================================

数据结构与算法之美-堆的应用

堆的应用一:优先级队列 优先级队列首先应该是一个队列.队列最大的特性就是先进先出.但是在优先级队列中,出队顺序不是先进先出,而是按照优先级来,优先级最高的,最先出队. 用堆来实现优先级队列是最直接.最高效的.这是因为,堆和优先级队列非常相似.一个堆就可以看作一个优先级队列.很多时候,它们只是概念上的区分而已. 往优先级队列中插入一个元素,就相当于往堆中插入一个元素.从优先级队列中取出优先级最高的元素,就相当于取出堆顶元素. 很多数据结构和算法都要依赖它.比如,赫夫曼编码.图的最短路径.最小生成树

JavaScript 数据结构与算法之美 - 栈内存与堆内存 、浅拷贝与深拷贝

前言 想写好前端,先练好内功. 栈内存与堆内存 .浅拷贝与深拷贝,可以说是前端程序员的内功,要知其然,知其所以然. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算法和方便以后复习. 栈 定义 后进者先出,先进者后出,简称 后进先出(LIFO),这就是典型的栈结构. 新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另一端就叫栈底. 在栈里,新元素都靠近栈顶,旧元素都接近栈底. 从栈的操作特性来看,是一种 操作受限的线性表,只允许在

Java数据结构和算法(一):综述

数据结构和算法能起到什么作用? 数据结构是指数据在计算机内存空间或磁盘中的组织形式.数据结构包括数组.链表.栈.二叉树.哈希表等等.算法对这些结构中的数据进行各种处理,例如,查找一条特殊的数据项或对数据进行排序. 数据结构的概述 数据结构 优点 缺点 数组 插入快,如果知道下标,可以非常快地存取 查找慢,删除慢,大小固定 有序数组 比无序数组查找快 删除和插入慢,大小固定 栈 提供后进先出方式的存取 存取其他项很慢 队列 提供先进先出方式的存取 存取其他项很慢 链表 插入快,删除快 查找慢 二叉

python 下的数据结构与算法---1:让一切从无关开始

我也忘了大概多久了,好像是三周多一点,终于把Data Structure and Algorithms with python以及 Problem Solving with  Algorithms and DataStructures看完了(图那部分没仔细看,太难太费时间,毕业设计开始了,有点忙).[github地址,包含了那两本书带笔记版以及下面零的代码] 所以啦,这作为第一篇总结笔记就从点无关的开始吧(也就是这两本书中提到的python相关但是与数据结构和算法无关的东东) 目录: 零:有些什

面试常考数据结构与算法

数据结构部分: 1.数组和链表的区别.(很简单,但是很常考,记得要回答全面) C++语言中可以用数组处理一组数据类型相同的数据,但不允许动态定义数组的大小,即在使用数组之前必须确定数组的大小.而在实际应用中,用户使用数组之前无法确定数组的大小,只能够将数组定义成足够大小,这样数组的空间可能不被使用,从而造成内存空间的浪费.链表是一种常见的数据组织形式,他采用动态分配内存的形式实现.需要时可以用new分配内存空间,不需要时用delete将已分配的空间释放,不会造成内存空间的浪费. 从逻辑结构上来看

数据结构与算法系列 目录

最近抽空整理了"数据结构和算法"的相关文章.在整理过程中,对于每种数据结构和算法分别给出"C"."C++"和"Java"这三种语言的实现:实现语言虽不同,但原理如出一辙.因此,读者在了解和学习的过程中,择其一即可! 下面是整理数据数据和算法的目录表,对于每一种按照C/C++/Java进行了划分,方便查阅.若文章有错误或纰漏,请不吝指正.谢谢! 数据结构和算法目录表   C C++ Java 线性结构 1. 数组.单链表和双链表

【Java数据结构学习笔记之二】Java数据结构与算法之队列(Queue)实现

  本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型   队列同样是一种特殊的线性表,其插入和删除的操作分别在表的两端进行,队列的特点就是先进先出(First In First Out).我们把向队列中插入元素的过程称为入队(Enqueue),删除元素的过程称为出队(Dequeue)并把允许入队的一端称为队尾,允许出的的一端称为队头,没有任何元素的队列

常见数据结构与算法整理总结(上)

数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作.算法是为求解一个问题需要遵循的.被清楚指定的简单指令的集合.下面是自己整理的常用数据结构与算法相关内容,如有错误,欢迎指出. 为了便于描述,文中涉及到的代码部分都是用Java语言编写的,其实Java本身对常见的几种数据结构,线性表.栈.队列等都提供了较好的实现,就是我们经常用到的Java集合框架,有需要的可以阅读这篇文章.Java - 集合框架完全解析 一.线性表 1.数组实现 2.链表 二.栈与队列 三.树