总结第K大相关问题
第K大数
题意非常简单,给出一堆乱序的数,让你找出这堆数中第k大的数
排序法
直接对这一堆乱序的数进行排序,然后取出第k大的数。这样做的时间复杂度是O(nlogn)
建堆法
首先对这些数建一个最大堆,然后再对最大堆堆顶的数pop k次,因为建堆的时间复杂度是O(4n),所以整体的时间复杂度也是O(4n + klogn)
快排法
利用快排算法的思想进行变形,每次在数组中随机找一个拆分数,类似快排的思想把这个拆分数放到数组中应该存在的位置,判断拆分数排列第几,如果在k的后面表示第k个数需要在前半部分找;如果在k的前面表示需要后面半部分找。从而使规模一步一步缩小。
最后可以算出时间复杂度期望为O(n),因为这取决于你拆分数的选取。
BFPRT算法
前面说了快排法能够使得其时间复杂度到达期望为O(n),那能不能使得时间复杂度严格为O(n)呢?可以的,这里就引出了BFPRT算法。
首先我们可以来看一下BFPRT算法的步骤:
- 分组(5个一组)
- 小组内排序(对于每一组的排序的时间复杂度是O(1))
- 把每个组的中位数拿出来组成一个新的数组
- 使用BFPRT递归算出这个中位数组的中位数
- 用这个当做划分值进行划分
- 划分成两边以后递归调用直到找到第k个数
原理:
通过这样选到的划分数一定在0.3N-0.7N之间,如图所示
每次选取到的划分数一定都比红色的数要大,比蓝色的数要小
这样时间复杂度的迭代公式就是:
通过推导不难算出其时间复杂度严格为O(n)
前K大数
求前K大的数就无法做到时间复杂度为O(n)了,但是方法是相似的
最小堆法
- 首先构造一个大小为k的最小堆
- 从k+1个数开始,当比堆顶大,加入堆中并维持堆大小为k(维持可以通过限定索引做到)
- 这样遍历了所有的数以后,堆中的这k个数就是前k个数了
快排法
依然可以用快排法
只不过在找到第k大的数之后需要对后面比这个数大的数进行一遍排序
参考: