动态数据集合中求top k大元素

  1. 1大,第2 ...第k
  2. k是这群体里最小的
  3. 所以要建立个小顶堆
  4. 只需要维护一个大小为k的小顶堆 即可
  5. 当来的元素(newCome)> 堆顶元素(smallTop),说明进来的元素有和堆顶竞争的资格,此时的堆顶被踢出 这时把进来的元素放到堆顶
  6. newCome>smallTop,smallTop的左右孩子>smallTop,所以无法确认 newComesmallTop的左右孩子的大小关系,
  7. newComesmallTop的左右子节点找到最小的元素和newCome交换,然后继续比较newCome与被交换的左右孩子的大小关系
  8. 持续这个过程(堆化)即可

如果每次询问前K大数据,我们都基于当前的数据重新计算的话,那时间复杂度就是O(nlogK),n表示当前的数据的大小

部分代码

topn.php

  1. $static_data=[2,5,3,1,0,7,6,10];
  2. //第3大
  3. /*
  4. 2,5,3 2
  5. 2,5,3 1 2
  6. 2,5,3,1,0 2
  7. 2,5,3,1,0,7 3
  8. 2,5,3,1,0,7,6 5
  9. 2,5,3,1,0,7,6,10 6
  10. 维持1个小顶堆 大小为3即可
  11. */
  12. $heap=new Heap(3); //建立一个大小为3的小顶堆
  13. foreach ($static_data as $v){
  14. echo $heap->topn($v).PHP_EOL;
  15. }

heap.php

  1. public function topn($data)
  2. {
  3. //堆满了
  4. if ($this->isFull()) {
  5. if ($data > $this->dataArr[1]) {
  6. $this->dataArr[1] = $data;
  7. $this->smallHeapFirst();
  8. }
  9. } else {
  10. $this->dataArr[$this->count + 1] = $data;
  11. $this->count++;
  12. $this->smallHeapLast();
  13. }
  14. return $this->dataArr[1];
  15. }

完整代码

动态数据流求中位数

  1. 2,3,1,7,5 返回3
  2. 1,3,1,7,5,4 返回3,4
  3. 数据持续往里面进,每进来一个数,就询问中位数是谁们

step1 思路分析:


  1. 所谓中位数,就是中间大的1个或者2个元素,中位数满足的性质,中位数之前的数都它,之后的数都大于它
  2. 先以奇数个分析,偶数个原理一样
  3. 1.如果是固定的数据集合,比如数据为n个,中位数即为n/2+1 大的元素,此时只需维护一个大小为(n/2+1) 大小的小顶堆即可
  4. 为什么不能是大顶堆呢,如果堆顶最大,除了知能找到这群集合的最大值外,其它的都无从知晓了
  5. 如果是小顶堆,堆顶最小,数据集合比如为5个,第3大的元素肯定小于已经比较过的前2个数,即为中间元素
  6. 但是现在是动态数据流,每次进来1个元素,都会询问中间元素
  7. 和静态数据的区别是:不知道维护的小顶堆的大小了
  8. 这时需要维护2个堆了,来了数据,分别放到这2个堆
  9. 1个大顶堆,1个小顶堆,大顶堆的数据均小于小顶堆的数据,当要询问的时候
  10. 如果是偶数个数据,两个堆的堆顶元素即为中间元素
  11. 如果奇数个数据,两个堆中数据较多的那个堆的堆顶元素即为中间元素

step1 步骤分析

  1. 大顶堆为big,堆顶元素bigpeak,大小为bigsize,小顶堆称small,堆顶元素为smallpeak,大小为smallsize
  2. 进来1个元素,big为空 :放入big
  3. big不为空:
  4. 放入元素<bigpeak,放入到big
  5. 放入元素>bigpeak, 放入到small
  6. 放入1个元素完成后
  7. 如果bigsize-smallsize>1,把big元素的堆顶元素拿掉 堆化big,把拿掉的元素放入small 然后堆化
  8. 如果bigsize-smallsize<1,把small元素的堆顶元素拿掉 堆化small,把拿掉的元素放入big 然后堆化

findmiddle.php

  1. $arr = [9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10];
  2. //$arr=[9,8,11,4,2,6,5,100];
  3. findMiddle($arr);
  4. //动态数据实时获取中位数
  5. function findMiddle($arr)
  6. {
  7. //大顶堆
  8. $bigHeap = new Heap(0, 1);
  9. //小顶堆
  10. $smallHeap = new Heap(0, 0);
  11. foreach ($arr as $k => $v) {
  12. if ($bigHeap->isEmpty()) {
  13. $bigHeap->insert($v);
  14. } else {
  15. $bigPeak = $bigHeap->peak();
  16. if ($v < $bigPeak) {
  17. $bigHeap->insert($v);
  18. } else {
  19. $smallHeap->insert($v);
  20. }
  21. if ($bigHeap->count - $smallHeap->count > 1) {
  22. $bigPeak = $bigHeap->deleteFirst();
  23. $smallHeap->insert($bigPeak);
  24. } elseif ($smallHeap->count - $bigHeap->count > 1) {
  25. $smallPeak = $smallHeap->deleteFirst();
  26. $bigHeap->insert($smallPeak);
  27. }
  28. }
  29. //实时获取中位数
  30. echo implode(',', midPeak($bigHeap, $smallHeap)) . PHP_EOL;
  31. }
  32. }
  33. function midPeak($heap1, $heap2)
  34. {
  35. if ($heap1->count == $heap2->count) {
  36. $midArr = [$heap1->peak(), $heap2->peak()];
  37. } elseif ($heap2->count > $heap1->count) {
  38. $midArr = [$heap2->peak()];
  39. } else {
  40. $midArr = [$heap1->peak()];
  41. }
  42. return $midArr;
  43. }

过程分析

几个重要的点
  • 两个堆元素数相等时中间元素为两个堆顶

    否者为较多元素堆的堆顶
  • 两者元素个数差值大于1时,要调整堆的元素个数
  1. 依次插入的元素 9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10,大顶堆 称为big,小顶堆称为small,各自大小bigsize,smallsize,堆顶为bigpeaksmallpeak,
  2. 9进来 big为空,插入big, bigsize-smallsize=1 不大于1
  3. 此时bigsize>smallsize 中间元素为bigpeak即为[9]
  4. 8进来 8<bigpeak, 插入big,bigsize-smallsize=2 大于1
  5. 此时bigpeak 需要从Big删除,big堆化,放入到small small堆化 ,此时bigsize=smallsize 所以中间元素为[bigpeak,smallpeak] 即为[8,9]
  6. 11进来 11>bigpeak(8),11插入small,此时smallsize=2,bigsize=1,差值不大于1,因为smallsize>bigsize,中间元素为[smallpeak] 即为[9]
  7. 4进来 4<bigpeak(8),4插入到big,big堆化,此时bigsize=2,smallsize=2,中间元素为[bigpeak,smallpeak] 即为[8,9]

此时堆图

  1. 2进来 2<8 ,2插入big然后堆化,bigsize=3,smallsize=2 所以此时中位数为[8]
  2. 6进来 6<8,6插入big后堆化 为下图

  1. 此时,bigsize=4,smallsize=2,bigsize-smallsize>1,删除big的堆顶元素 堆化,然后把把删除的元素插入到small,堆化后
  2. 此时big,small见下图,中间元素位[bigpeak,smallpeak]即 [6,8]

  1. 5进来 5<bigpeak(8),5插入big堆化
  2. 此时Bigsize=4,smallsize=3,差值不大于1,中间元素位bigpeak 即为[6]
  3. 之后的步骤同理

插入数据因为需要涉及堆化,所以时间复杂度变成了O(logn),但是求中位数我们只需要返回大顶堆的堆顶元素就可以了,所以时间复杂度就是O(1)

完整代码

堆实战(动态数据流求top k大元素,动态数据流求中位数)的更多相关文章

  1. 算法导论学习之线性时间求第k小元素+堆思想求前k大元素

    对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...

  2. 面试题:求第K大元素(topK)?

    一.引言二.普通算法算法A:算法B:三.较好算法算法C:算法D:四.总结 一.引言 ​ 这就是类似求Top(K)问题,什么意思呢?怎么在无序数组中找到第几(K)大元素?我们这里不考虑海量数据,能装入内 ...

  3. ACM_求第k大元素(两次二分)

    求第k大 Time Limit: 6000/3000ms (Java/Others) Problem Description: 给定两个数组A和B,大小为N,M,每次从两个数组各取一个数相乘放入数组C ...

  4. 行列有序矩阵求第k大元素

    问题来源:http://www.careercup.com/question?id=6335704 问题描述: Given a N*N Matrix. All rows are sorted, and ...

  5. 【Leetcode堆】数据流中的第K大元素(703)

    题目 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数 ...

  6. Leetcode 703. 数据流中的第K大元素

    1.题目要求 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器, ...

  7. [Swift]LeetCode703. 数据流中的第K大元素 | Kth Largest Element in a Stream

    Design a class to find the kth largest element in a stream. Note that it is the kth largest element ...

  8. 数据流中的第k大元素的golang实现

    设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中 ...

  9. [LeetCode解题报告] 703. 数据流中的第K大元素

    题目描述 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包 ...

随机推荐

  1. IT技术管理者的自我修养

    1. 前言 本来写<IT技术管理者的自我修养>与<IT技术人员的自我修养>是一开始就有的想法.但发表<IT技术人员的自我修养>后,收到了不少良好的反馈,博客园的编辑 ...

  2. Windows 纠错

    4:在Windows应用程序中,当需要将窗体显示为模式对话框时,需要调用窗体的()方法.(选择一项)A:Activate()B:ShowDialog()C:Show()D:Close()正确答案是 B ...

  3. HTML第六章 盒子模型

    什么是盒子模型: (1)边框: (2)内边距: (3)外边距: (4)元素内容·: (5)背景色·: 边框: 属性: 颜色(border-color),粗细(border-width),样式(bord ...

  4. (数据科学学习手札66)在ubuntu服务器上部署shiny

    一.简介 shiny是R中专门用于开发轻量级web应用的框架,在本地写一个shiny应用并调用非常方便,但如果你希望你的shiny应用能够以远程的方式提供给更多人来使用,就需要将写好的shiny应用部 ...

  5. C#_会员管理系统

    https://www.cnblogs.com/start-from-scratch/p/5420588.html

  6. SpringBoot打包部署简单说明

    SpringBoot项目打包部署 一.jar包方式 这种使用SpringBoot内嵌的Tomcat进行部署 打包方式默认jar,所以下面加也行,不加也行 <packaging>war< ...

  7. springboot启动慢解决方法

    jdk的配置文件中,使用securerandom.source设置了熵源: cat /usr/java/jdk1.8.0_121/jre/lib/security/java.security secu ...

  8. 使用flask-restful搭建API

    最简单的例子 ---~~~~ 访问http://127.0.0.1:5000/ , 返回{"hello": "world"} from flask import ...

  9. C# - 协变、逆变 看完这篇就懂了

    1. 基本概念 官方:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型.[MSDN] 公式: ...

  10. pt-online-schema-change使用详解

    一.pt-online介绍 pt-online-schema-change是percona公司开发的一个工具,在percona-toolkit包里面可以找到这个功能,它可以在线修改表结构 原理: 首先 ...