目录
前言
堆最大的特点是最大值或最小值位于堆的顶部,只需要 O(1) 的时间就可以求出一个数据集合中的最大值或最小值,同时在堆中添加或删除元素的时间复杂度都是 O(logn),因此综合来看堆是一个比较高效的数据结构。如果面试题需要求出一个动态数据集合中的最大值或最小值,那么可以考虑使用堆来解决问题。
堆经常用来求取一个数据集合中值最大或最小的 k 个元素。通常,最小堆用来求取数据集合中 k 个值最大的元素,最大堆用来求取数据集合中 k 个值最小的元素。
接下来使用最小堆或最大堆解决几道典型的算法面试题。
面试题 59 : 数据流的第 k 大数字
题目:
请设计一个类型 KthLargest,它每次从一个数据流中读取一个数字,并得出数据流已经读取的数字中第 k(k >= 1)大的数字。该类型的构造函数有两个参数:一个是整数 k,另一个是包含数据流中最开始数字的整数数组 nums。该类型还有一个函数 add,用来添加数据流中的新数字并返回数据流中已经读取的数字的第 k 大数字。
例如,当 k = 3 且 nums 为数组 [4, 5, 8, 2] 时,调用构造函数创建类型 KthLargest 的实例之后,第 1 次调用 add 函数添加数字 3,此时已经从数据流中读取了数字 4、5、8、2 和 3,第 3 大的数字是 4;第 2 次调用 add 函数添加数字 5 时,则返回第 3 大的数字 5。
分析:
与数据流相关的题目的特点是输入的数据是动态添加的,也就是,可以不断地从数据流中读取新的数据,数据流的数据量是无限的。在这个题目中,类型 KthLargest 的函数 add 用来添加从数据流中读取的新数据。
解决这个题目的关键在于选择合适的数据结构。如果数据存储在排序的数组中,那么只需要 O(1) 的时间就能找出第 k 大的数字。但这个直观的方法有两个缺点。首先,需要把从数据流中读取的所有数据都存到排序数组中,如果从数据流中读取 n 个数字,那么动态数组的大小为 O(n)。随着不断地从数据流中读取新的数据,O(n) 的空间复杂度可能会耗尽所有的内存。其次,在排序数组中添加新的数字的时间复杂度也是 O(n)。
下面换一个角度看待第 k 大的数字。如果能够找出 k 个最大的数字,那么第 k 大的数字就是这 k 个最大数字中最小的一个。例如,从数据流中已经读出了 4、5、8、2、3 这 5 个数字,其中最大的 3 个数字是 4、5、8。这 3 个数字的最小值 4 就是 4、5、8、2、3 这 5 个数字中第 3 大的数字。
由于每次都需要找出 k 个数字中的最小值,因此可以把这 k 个数字保存到最小堆中。每当从数据流中读出一个数字,就先判断这个新的数字是不是有必要添加到最小堆中。
-
如果最小堆中元素的数目还小于 k,那么直接将它添加到最小堆中。
-
如果最小堆中已经有 k 个元素,那么将其和位于堆顶的最小值进行比较。如果新读出的数字小于或等于堆中的最小值,那么堆中的 k 个数字都比它大,因此它不可能是 k 个最大的数字中的一个。由于只需要保存最大的 k 个数字,因此新读出的数字可以忽略。如果新的数字大于堆顶的数字,那么堆顶的数字就是第 k + 1 大的数字,可以将它从堆中删除,并将新的数字添加到堆中,这样堆中保存的仍然是到目前为止从数据流中读出的最大的 k 个数字,此时第 k 大的

文章介绍了如何利用堆(最小堆和最大堆)解决数据流中的三个问题:1.在数据流中找到第k大的数字;2.找出数组中出现频率最高的k个数字;3.从两个递增排序数组中找到和最小的k个数对。通过优化数据结构和算法,保证了较高的效率。
1309

被折叠的 条评论
为什么被折叠?



