前言
在以前的
工作或面试中常常会遇到的一个问题,如何实现大规模的TopN,是一个非常大的结果集,并迅速找到前10或前100的数量,同时确保
内存和
速度的效率,我们可能第一个想法是使用
排序,然后10拦截或前100,而排名的体积不是特别大,没有任何问题,但只要大量来完成这项
任务是不可能的,例如,有一个数组或一个
文本文件,数以百万计的数字,这不是全部读入内存,所以排名不
解决这一问题的最佳方案,这里我们使用PHP实现一小堆的顶部来解决这个问题。
两叉桩
二叉堆是一个特殊的堆,两叉堆是完全二叉树或接近完全二叉树,两叉堆二,最大堆和最小堆,最大堆:父节点值总是大于或等于任何子节点键;最小堆:父节点密钥总数小于或等于任何子节点键。
小堆顶(
图片来自
网络)
一个数组来表示二叉堆一般使用(见图表),例如,在数组中的
位置是根节点0,节点位置分别在第n 2n+1和2n + 2,因此,零阶的子节点在1和2, 1在3和4的子节点的位置,和所以,这种存储方式查找父节点和子节点。
这里的概念是不是说,如果有问题的二叉堆可以在一个好的数据结构,我们有以下的TopN使用PHP代码和解决,为了看到这里的差异去下的排序结果的
方法。
使用快速排序算法实现TopN
为了测试/
运行内存
ini_set('memory_limit ','2024m);
实现快速排序
功能 功能quick_sort(数组){
长度=计数(数组);
left_array美元=阵();
right_array美元=阵();
如果(长度< = 1){
返回数组;
}
$ = $数组{ 0 };
($ i = 1;$ i $ $长度;$ + +){
如果($ $)
right_array美元{ } =数组{ $我};
其他{ }
left_array美元{ } =数组{ $我};
}
}
left_array = quick_sort美元(美元left_array);
right_array = quick_sort美元(美元right_array);
返回array_merge($ right_array,阵列($键),left_array美元);
}
500w重复数 / /结构
($ i = 0;$ i < 5000000;$ + +){
numarr美元{ } = $我;
}
他们扰乱 /
洗牌($ numarr);
我们
发现TOP10 / /现在从内数量最多。
var_dump(时间());
print_r(array_slice(quick_sort(合所有),0));
var_dump(时间());
运行后的结果
你可以看到,上面
打印的TOP10的结果,输出下的运行时间,99s左右,但这只是500w数都可以被加载到内存中。如果我们有5亿5kW或文件中的数字,会有一些问题。
采用二叉堆算法实现TopN
实施过程是:
1、第一次读到10或100数字的数组,这是我们的流量数。
2。调用生成一个小顶堆
函数,并生成一个小顶堆结构,此时必须是最小值。
三.所有剩余的数字都从文件或数组中遍历。
4。每个遍历一个都与堆顶部的大小进行比较。如果它小于堆顶元素,如果它大于堆顶元素,它将被
替换。
5。在替换堆顶元素之后,通过调用生成的小堆函数来生成小顶堆,因为需要找到最小的顶堆函数。
6、重复
步骤4 ~ 5,当遍历结束,我们没有在堆的顶部是最大的流量,因为小顶堆总是把最出的最小,与顶尖的快速
调整,只有相对的调整,只要根节点小于。节点可以是。
7,在最坏
情况下的TOP10算法的复杂度,其中每个遍历一个号码,如果更换堆栈顶部,需要调整至10倍,比排序的速度,但也不是所有的内容都读到内存中,可以了解一个线性遍历的成果。
在堆的顶部生成一个小函数
功能堆($ ARR,$ IDX){
为左=(美元指数< 1)+ 1;
$权=($ IDX << 1)+ 2;
如果(!ARR {左}美元美元){
返回;
}
如果($ ARR { $权} { } <对ARR美元美元美元美元ARR {左}){
$ l = $;
其他{ }
美元=左;
}
If ($arr{$idx} > $arr{$l}) {
TMP =美元美元美元IDX ARR { };
ARR { } =美元美元美元美元IDX ARR { L };
{ } = L ARR美元美元美元的TMP;
堆($ ARR,$ L);
}
}
为了保证在上述 /一致性,也不构成500w重复数
*
当然,这个数据集并不总是存储在内存中,它也可以在
在文件中,因为我们没有全部加载到内存中
行排序
* /
($ i = 0;$ i < 5000000;$ + +){
numarr美元{ } = $我;
}
他们扰乱 /
洗牌($ numarr);
取出数组10
toparr美元= array_slice(numarr美元,10);
获取最后一个子节点索引位置
因为在构建堆的小/顶部时,是从最后一个左或右节点位置构建的。
从开始到移动结构(图表的顶部)
$ IDX =地板(计数($ toparr) / 2)- 1;
生成一个小的堆顶
为($我= $ IDX;我> = 0美元美元;我--){
堆(toparr美元美元,我);
}
var_dump(时间());
在这里您可以看到,其余的
都是元素遍历的开始。
为($我=计数($ toparr);$我<计数(numarr美元美元);i++){
每一个都是对遍历/堆栈元素进行比较大小。
如果($ numarr { $我} > $ toparr { 0 }){
如果超过了堆/元素替换的顶部
为toparr { 0 } = {我} numarr美元美元;
*
再活化生成小顶堆函数进行维修,但这次是从堆的顶部
指数的位置开始从顶部保持,因为我们只是把顶部的桩。
根据根节点替换元素,其余元素小于左节点和右节点。
这就是我们所说的,这只是一个相对的调整。
不是所有的调整
* /
堆(toparr美元,0);
}
}
var_dump(时间());
运行后的结果
你可以看到结果是TOP10,但时间只有约1s,与记忆和时间效率能满足我们的要求,而且对排名比最好的事情是,不是所有的数据集的读为记忆,因为我们不需要排序,与上面的论证,所以直接在500W元素的存储结构,但我们可以把它全部转移到文件,然后读取一行比较,因为我们的高级数据结构的小
核心是线性的非常小的内存遍历器内部结构进行比较,最后TopN。
总结
最后一点要说的是算法+数据结构是非常重要的,一个好的算法可以大大提高我们的效率。希望本文的内容能给大家的
学习或工作带来一定的帮助。如果有任何疑问,您可以留言交流,谢谢您的
支持。