堆实战(动态数据流求top k大元素,动态数据流求中位数)
动态数据集合中求top k大元素
第1大,第2大 ...第k大
k是这群体里最小的
所以要建立个小顶堆
只需要维护一个大小为k的小顶堆 即可
当来的元素(newCome)> 堆顶元素(smallTop),说明进来的元素有和堆顶竞争的资格,此时的堆顶被踢出 这时把进来的元素放到堆顶
newCome>smallTop,smallTop的左右孩子>smallTop,所以无法确认 newCome和smallTop的左右孩子的大小关系,
在newCome和smallTop的左右子节点找到最小的元素和newCome交换,然后继续比较newCome与被交换的左右孩子的大小关系
持续这个过程(堆化)即可
如果每次询问前K大数据,我们都基于当前的数据重新计算的话,那时间复杂度就是O(nlogK),n表示当前的数据的大小
部分代码
topn.php
$static_data=[2,5,3,1,0,7,6,10];
//第3大
/*
2,5,3 2
2,5,3 1 2
2,5,3,1,0 2
2,5,3,1,0,7 3
2,5,3,1,0,7,6 5
2,5,3,1,0,7,6,10 6
维持1个小顶堆 大小为3即可
*/
$heap=new Heap(3); //建立一个大小为3的小顶堆
foreach ($static_data as $v){
echo $heap->topn($v).PHP_EOL;
}
heap.php
public function topn($data)
{
//堆满了
if ($this->isFull()) {
if ($data > $this->dataArr[1]) {
$this->dataArr[1] = $data;
$this->smallHeapFirst();
}
} else {
$this->dataArr[$this->count + 1] = $data;
$this->count++;
$this->smallHeapLast();
}
return $this->dataArr[1];
}
动态数据流求中位数
2,3,1,7,5 返回3
1,3,1,7,5,4 返回3,4
数据持续往里面进,每进来一个数,就询问中位数是谁们
step1 思路分析:
所谓中位数,就是中间大的1个或者2个元素,中位数满足的性质,中位数之前的数都它,之后的数都大于它
先以奇数个分析,偶数个原理一样
1.如果是固定的数据集合,比如数据为n个,中位数即为n/2+1 大的元素,此时只需维护一个大小为(n/2+1) 大小的小顶堆即可
为什么不能是大顶堆呢,如果堆顶最大,除了知能找到这群集合的最大值外,其它的都无从知晓了
如果是小顶堆,堆顶最小,数据集合比如为5个,第3大的元素肯定小于已经比较过的前2个数,即为中间元素
但是现在是动态数据流,每次进来1个元素,都会询问中间元素
和静态数据的区别是:不知道维护的小顶堆的大小了
这时需要维护2个堆了,来了数据,分别放到这2个堆
1个大顶堆,1个小顶堆,大顶堆的数据均小于小顶堆的数据,当要询问的时候
如果是偶数个数据,两个堆的堆顶元素即为中间元素
如果奇数个数据,两个堆中数据较多的那个堆的堆顶元素即为中间元素
step1 步骤分析
大顶堆为big,堆顶元素bigpeak,大小为bigsize,小顶堆称small,堆顶元素为smallpeak,大小为smallsize
进来1个元素,big为空 :放入big
big不为空:
放入元素<bigpeak,放入到big
放入元素>bigpeak, 放入到small
放入1个元素完成后
如果bigsize-smallsize>1,把big元素的堆顶元素拿掉 堆化big,把拿掉的元素放入small 然后堆化
如果bigsize-smallsize<1,把small元素的堆顶元素拿掉 堆化small,把拿掉的元素放入big 然后堆化
findmiddle.php
$arr = [9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10];
//$arr=[9,8,11,4,2,6,5,100];
findMiddle($arr);
//动态数据实时获取中位数
function findMiddle($arr)
{
//大顶堆
$bigHeap = new Heap(0, 1);
//小顶堆
$smallHeap = new Heap(0, 0);
foreach ($arr as $k => $v) {
if ($bigHeap->isEmpty()) {
$bigHeap->insert($v);
} else {
$bigPeak = $bigHeap->peak();
if ($v < $bigPeak) {
$bigHeap->insert($v);
} else {
$smallHeap->insert($v);
}
if ($bigHeap->count - $smallHeap->count > 1) {
$bigPeak = $bigHeap->deleteFirst();
$smallHeap->insert($bigPeak);
} elseif ($smallHeap->count - $bigHeap->count > 1) {
$smallPeak = $smallHeap->deleteFirst();
$bigHeap->insert($smallPeak);
}
}
//实时获取中位数
echo implode(',', midPeak($bigHeap, $smallHeap)) . PHP_EOL;
}
}
function midPeak($heap1, $heap2)
{
if ($heap1->count == $heap2->count) {
$midArr = [$heap1->peak(), $heap2->peak()];
} elseif ($heap2->count > $heap1->count) {
$midArr = [$heap2->peak()];
} else {
$midArr = [$heap1->peak()];
}
return $midArr;
}
过程分析
几个重要的点
- 两个堆元素数相等时中间元素为两个堆顶
否者为较多元素堆的堆顶 - 两者元素个数差值大于1时,要调整堆的元素个数
依次插入的元素 为 9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10,大顶堆 称为big,小顶堆称为small,各自大小bigsize,smallsize,堆顶为bigpeak,smallpeak,
9进来 big为空,插入big, bigsize-smallsize=1 不大于1
此时bigsize>smallsize 中间元素为bigpeak即为[9]
8进来 8<bigpeak, 插入big,bigsize-smallsize=2 大于1
此时bigpeak 需要从Big删除,big堆化,放入到small ,small堆化 ,此时bigsize=smallsize 所以中间元素为[bigpeak,smallpeak] 即为[8,9]
11进来 11>bigpeak(8),11插入small,此时smallsize=2,bigsize=1,差值不大于1,因为smallsize>bigsize,中间元素为[smallpeak] 即为[9]
4进来 4<bigpeak(8),4插入到big,big堆化,此时bigsize=2,smallsize=2,中间元素为[bigpeak,smallpeak] 即为[8,9]
此时堆图
2进来 2<8 ,2插入big然后堆化,bigsize=3,smallsize=2 所以此时中位数为[8]
6进来 6<8,6插入big后堆化 为下图
此时,bigsize=4,smallsize=2,bigsize-smallsize>1,删除big的堆顶元素 堆化,然后把把删除的元素插入到small,堆化后
此时big,small见下图,中间元素位[bigpeak,smallpeak]即 [6,8]
5进来 5<bigpeak(8),5插入big堆化
此时Bigsize=4,smallsize=3,差值不大于1,中间元素位bigpeak 即为[6]
之后的步骤同理
插入数据因为需要涉及堆化,所以时间复杂度变成了O(logn),但是求中位数我们只需要返回大顶堆的堆顶元素就可以了,所以时间复杂度就是O(1)
堆实战(动态数据流求top k大元素,动态数据流求中位数)的更多相关文章
- 算法导论学习之线性时间求第k小元素+堆思想求前k大元素
对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...
- 面试题:求第K大元素(topK)?
一.引言二.普通算法算法A:算法B:三.较好算法算法C:算法D:四.总结 一.引言 这就是类似求Top(K)问题,什么意思呢?怎么在无序数组中找到第几(K)大元素?我们这里不考虑海量数据,能装入内 ...
- ACM_求第k大元素(两次二分)
求第k大 Time Limit: 6000/3000ms (Java/Others) Problem Description: 给定两个数组A和B,大小为N,M,每次从两个数组各取一个数相乘放入数组C ...
- 行列有序矩阵求第k大元素
问题来源:http://www.careercup.com/question?id=6335704 问题描述: Given a N*N Matrix. All rows are sorted, and ...
- 【Leetcode堆】数据流中的第K大元素(703)
题目 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数 ...
- Leetcode 703. 数据流中的第K大元素
1.题目要求 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器, ...
- [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 ...
- 数据流中的第k大元素的golang实现
设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中 ...
- [LeetCode解题报告] 703. 数据流中的第K大元素
题目描述 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包 ...
随机推荐
- java的jar打包工具的使用
java的jar打包工具的使用 java的jar是一个打包工具,用于将我们编译后的class文件打包起来,这里面主要是举一个例子用来说明这个工具的使用. 在C盘下的temp文件夹下面: ...
- 监控LVS
监控LVS #!/usr/bin/python-2.6.6 #data 2017-10-17 #auth liuchao import commands,os,time #-------------- ...
- Git命令备忘录
目录 前言 基本内容 开始之前 基础内容 远程仓库 分支管理 前言 Git在平时的开发中经常使用,整理Git使用全面的梳理. 基本内容 开始之前 请自行准备好Git工具以及配置好Git的基本配置 基础 ...
- 统一流控服务开源:基于.Net Core的流控服务
先前有一篇博文,梳理了流控服务的场景.业界做法和常用算法 统一流控服务开源-1:场景&业界做法&算法篇 最近完成了流控服务的开发,并在生产系统进行了大半年的验证,稳定可靠.今天整理一下 ...
- 2月11日 阿里巴巴Java开发手册 读后感
该手册分为几个部分: 印象深刻的几点: (五)集合处理 2.[强制]ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常:java. ...
- 机器学习中的误差 Where does error come from?
误差来自于偏差和方差(bias and variance) 对于随机变量 X,假设其期望和方差分别为 μ 和 σ2.随机采样 N 个随机变量构成样本,计算算术平均值 m,并不会直接得到 μ (除非 ...
- html学习笔记整理
网页 1.网页的组成部分 网页是由文字,图片,视频,音频,输入框,按钮这些元素(也就是html标签)组成. 2.浏览网页常用的五大主流浏览器 谷歌,IE,火狐,欧朋,safari.浏览器的内核(渲染引 ...
- 浅谈osi模型 三次握手 四次挥手 ddos攻击原理
C/S B/S 架构 C:client 端 B:browser 浏览器 S:server 端 C/S架构,基于客户端与服务端之间的通信 例如:QQ,抖音,快手,微信,支付宝等等 优点:个性化设置,响应 ...
- html5 placeholder属性兼容ie11
placeholder 属性是html5的属性,用于提供描述输入字段预期值的提示信息(hint). 简单例子: <!DOCTYPE HTML> <html> <body& ...
- 《统计学习方法》极简笔记P2:感知机数学推导
感知机模型 输入空间是$\chi\subseteq\mathbb{R}^n$,输出空间是$y={+1,-1}$ 感知机定义为:$f(x)=sign(wx+b)$ 感知机学习策略 输入空间任一点$x_0 ...