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

堆的应用一:优先级队列

优先级队列首先应该是一个队列。队列最大的特性就是先进先出。但是在优先级队列中,出队顺序不是先进先出,而是按照优先级来,优先级最高的,最先出队。

用堆来实现优先级队列是最直接、最高效的。这是因为,堆和优先级队列非常相似。一个堆就可以看作一个优先级队列。很多时候,它们只是概念上的区分而已。

往优先级队列中插入一个元素,就相当于往堆中插入一个元素。从优先级队列中取出优先级最高的元素,就相当于取出堆顶元素。

很多数据结构和算法都要依赖它。比如,赫夫曼编码、图的最短路径、最小生成树算法等等。


合并有序小文件

假设我们有 100 个小文件,每个文件的大小都一样,每个文件中存储的都是有序的字符串。

我们希望将这些100 个小文件合并成一个有序的大文件。这里就会用到优先级队列。

我们从这 100 个文件中,各取第一个字符串,入到小顶堆中,那堆顶的元素,也就是优先级队列队首的元素,就是最小的字符串。我们将这个字符串放入到大文件中,并将其从堆中删除。然后再从小文件中取出下一个字符串,放入到堆中。循环这个过程,就可以将 100 个小文件中的数据依次放入到大文件中。


高性能定时器

假设定时器中维护了很多定时任务,每个任务都设定了一个要触发执行的时间点。

定时器每过一个很小的单位时间,就扫描一遍任务,看是否有任务到达设定的执行时间。

这样的做法比较低效,任务的时间离当前时间可能还有很久,很多次扫描其实都是徒劳的。每次都要扫描整个任务列表,比较耗时。

使用优先级队列来,我们按照任务设定的执行时间,将这些任务存储在优先级队列中,也小顶堆的堆顶存储的是最先执行的任务。

拿队首任务的执行时间点,与当前时间点相减,得到一个时间间隔T。这个时间间隔T就是指从当前时间开始需要等待多久才会有第一个任务需要被执行。

这样定时器就可以设定在 T 秒之后再来执行任务。从当前时间点到(T-1)秒这段时间里,定时器都不需要做任何事情。


堆的应用二:利用堆求 Top K

求Top K的问题可以抽象成两类。一类是针对静态数据集合,也就是说数据集合事先确定,不会再变。另一类是针对动态数据集合,也就是说数据集合事先并不确定,有数据动态地加入到集合中。

静态数据

维护一个大小为K的小顶堆,顺序遍历数组,从数组中取出数据与堆顶元素比较。

如果比堆顶元素大,我们就把堆顶元素删除,并且将这个元素插入到堆中。如果比堆顶元素小,则不做处理,继续遍历数组。

这样等数组中的数据都遍历完之后,堆的数据就是前 K 大数据了。

动态数据

一个数据集合中有两个操作,一个是添加数据,另一个询问当前的前K 大数据。如果每次询问前K大的数据,我们都基于当前的数据重新计算的话就浪费了性能。

可以一直都维护一个K大小的小顶堆,当有数据被添加到集合中时,就拿它与堆顶的元素对比。

如果比堆顶元素大就把堆顶元素删除,并且将这个元素插入到堆中。如果比堆顶元素小,则不做处理。

这样无论任何时候需要查询当前的前 K 大数据,都可以立刻返回。


堆的应用三:利用堆求中位数

如果数据的个数是奇数,那第n/2+1个数据就是中位数。如果数据的个数是偶数的话,第n/2个和n/2+1个这两个数据就是中位数。可以随意取一个作为中位数,如第n/2个。

静态数据

中位数是固定的,我们可以先排序,第n/2个数据就是中位数。

每次询问中位数的时候,直接返回这个固定的值。排序的代价比较大,但是边际成本很小。

动态数据

中位数在不停地变动,静态数据的方法每次询问中位数的时候,都要先进行排序的话效率不会高。

借助堆这种数据结构,不用排序就可以非常高效地实现求中位数。

新加入的数据小于等于大顶堆的堆顶元素,就将这个新数据插入到大顶堆。新加入的数据大于等于小顶堆的堆顶元素,我们就将这个新数据插入到小顶堆。

这个时候可能出现两个堆中的数据个数不符合前面约定的情况,这时可以从一个堆中不停地将堆顶元素移动到另一个堆,来让两个堆中的数据满足上面的约定。

利用两个堆,一个大顶堆一个小顶堆就可以实现在动态数据集合中求中位数的操作。

原文地址:https://www.cnblogs.com/errornull/p/10054750.html

时间: 2024-11-10 07:03:41

数据结构与算法之美-堆的应用的相关文章

数据结构与算法之美-堆和堆排序

堆和堆排序 如何理解堆 堆是一种特殊的树,只要满足以下两点,这个树就是一个堆. ①完全二叉树,完全二叉树要求除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列. ②树中每一个结点的值都必须大于等于(或小于等于)其子树中每个节点的值.大于等于的情况称为大顶堆,小于等于的情况称为小顶堆. 如何实现堆 如何存储一个堆 完全二叉树适合用数组来存储,因为数组中对于下标从1开始的情况,下标为i的节点的左子节点就是下标为i*2的节点,右子节点就是i下标为i*2+1的节点,其父节点时下标为i/2的

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

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

JavaScript 数据结构与算法之美 - 归并排序、快速排序、希尔排序、堆排序

1. 前言 算法为王. 想学好前端,先练好内功,只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算法和方便以后复习. 之所以把归并排序.快速排序.希尔排序.堆排序放在一起比较,是因为它们的平均时间复杂度都为 O(nlogn). 请大家带着问题:快排和归并用的都是分治思想,递推公式和递归代码也非常相似,那它们的区别在哪里呢 ? 来阅读下文. 2. 归并排序(Merge Sort) 思想 排序一个数

《数据结构与算法之美》——冒泡排序、插入排序、选择排序

排序,是每一本数据结构的书都绕不开的重要部分. 排序的算法也是琳琅满目.五花八门. 每一个算法的背后都是智慧的结晶,思想精华的沉淀. 个人觉得排序算法没有绝对的孰优孰劣,用对了场景,就是最有的排序算法. 当然,撇开这些业务场景,排序算法本身有一些自己的衡量指标,比如我们经常提到的复杂度分析. 我们如何分析一个算法? 排序算法的执行效率 1.最好.最坏和平均情况的时间复杂度 2.时间复杂度的系数.常数和低阶 一般来说,在数据规模n很大的时候,可以忽略这些,但是如果我们需要排序的数据规模在几百.几千

JavaScript 数据结构与算法之美 - 冒泡排序、插入排序、选择排序

1. 前言 算法为王. 想学好前端,先练好内功,只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算法和方便以后复习. 之所以把冒泡排序.选择排序.插入排序放在一起比较,是因为它们的平均时间复杂度都为 O(n2). 请大家带着问题:为什么插入排序比冒泡排序更受欢迎 ?来阅读下文. 2. 如何分析一个排序算法 复杂度分析是整个算法学习的精髓. 时间复杂度: 一个算法执行所耗费的时间. 空间复杂度:

《数据结构与算法之美》 <03>数组:为什么很多编程语言中数组都从0开始编号?

提到数组,我想你肯定不陌生,甚至还会自信地说,它很简单啊. 是的,在每一种编程语言中,基本都会有数组这种数据类型.不过,它不仅仅是一种编程语言中的数据类型,还是一种最基础的数据结构.尽管数组看起来非常基础.简单,但是我估计很多人都并没有理解这个基础数据结构的精髓. 在大部分编程语言中,数组都是从 0 开始编号的,但你是否下意识地想过,为什么数组要从 0 开始编号,而不是从 1 开始呢? 从 1 开始不是更符合人类的思维习惯吗? 你可以带着这个问题来学习接下来的内容. 如何实现随机访问? 什么是数

十分钟弄懂:数据结构与算法之美 - 时间和空间复杂度

复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容基本上就掌握了一半了. 1. 什么是复杂度分析 ? 数据结构和算法解决是 "如何让计算机更快时间.更省空间的解决问题". 因此需从执行时间和占用空间两个维度来评估数据结构和算法的性能. 分别用时间复杂度和空间复杂度两个概念来描述性能问题,二者统称为复杂度. 复杂度描述的是算法执行时间(或占用空间)与数据规模的增长关系. 2. 为什么要进行复杂度分析 ? 和性能测试相比,复杂度分析有不依赖执行环境.成本低.效率高.易操作.

java数据结构和算法10(堆)

这篇我们说说堆这种数据结构,其实到这里就暂时把java的数据结构告一段落,感觉说的也差不多了,各种常见的数据结构都说到了,其实还有一种数据结构是"图",然而暂时对图没啥兴趣,等有兴趣的再说:还有排序算法,emmm....有时间再看看吧! 其实从写数据结构开始到现在让我最大的感触就是:新手刚开始还是不要看数据结构为好,太无聊太枯燥了,很容易让人放弃:可以等用的各种框架用得差不多了之后,再回头静下心来搞搞数据结构还是挺有趣的:废话不多说,开始今天的内容: 1.二叉树分类 树分为二叉树和多叉

数据结构与算法之美-字符串匹配(上)

BF (Brute Force) 暴力/朴素匹配算法 主串和模式串 我们在字符串 A 中查找字符串 B,那字符串 A 就是主串,字符串 B 就是模式串. 我们把主串的长度记作 n,模式串的长度记作 m.因为我们是在主串中查找模式串,所以 n>m. BF算法思想 在主串中,检查起始位置分别是 0.1.2-n-m 且长度为 m 的 n-m+1 个子串,看有没有跟模式串匹配的. BF算法的缺点 在极端情况下,如主串是"aaaaa-aaaaaa",模式串是"aaaaab&quo