当前位置:网站首页>堆排序--TOP-K问题解决及复杂度分析
堆排序--TOP-K问题解决及复杂度分析
2022-04-21 13:55:00 【风住尘香花已尽0.0】
数组变堆:
上面的情况是在输入时直接对数组进行堆处理,下面是对一个给定的数组进行堆排序。
输入一个简单的无序数组:
int a[] = {9,0,3,5,6,7,8,2}
这个数组用二叉树表示为

改变此二叉树为堆有两种思路;第一种是向下调整,第二种是向上调整。
向下调整的思想为:
-
先对最后一个非叶子结点进行向下调整
-
对非叶子结点的上一个结点进行向下调整,直到调整到根节点结束

最终就会调整成一个最小堆。
向上调整思想:
- 找到根结点对根节点下一个结点进行向上调整
- 直到找到最后一个节点结束

代码实现:
//向上对数组建堆处理
void HeapInit1(int* a, int n) {
for (int i = 0; i < n; i++) {
AdjustUp(a, i);
}
}
//向下对数组建堆处理
void HeapInit2(int* a, int n) {
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
}
向上调整算法复杂度分析:
我们用一颗如图所示的满二叉树进行算法复杂度分析

第一层不用调整从第二层开始,第二层有21个结点,每个结点最坏的情况是调整1次**,所以**第二层次数为21*1,第三层有2^2个结点,每个结点最坏的情况是调整2次,以此类推。
假设向上调整次数设为T(h),T(h) = 2^1*1 + 2^2*2 + 2^3*3 + …… + 2^(h-1)*(h-1)
T(h) = 2^1*1 + 2^2*2 + 2^3*3 + …… + 2^(h-1)*(h-2)+ 2^h*(h-1)
2T(h) = 2^2*1 + 2^3*2 + …… + 2^(h-1)*(h-1)
用 2T(h) - T(h)
得到 T(h) = -2^0+(-2^1*1) + (-2^2)+(-2^3) + …… + (-2^(h-1)) + 2^h*(h-1)+ 2^0
T(h) = -(2^h-1) + 2^h*(h-1)+1 = 2^h*(h-2) + 2
结点个数N = 2^h-1, 层数h = log(N+1)
所以得到 T(n) = (N+1)(log(N+1)-2)+2
算法复杂度 O(n) = nlogn
向下调整复杂度分析:

第一层有20个结点**而最坏情况需要**向下移动h-1层**,第二层有**21个结点最坏情况下需要向下移动h-1层…倒数第二层有2^h-1个结点需要向下移动1层。
所以根据这种情况可以设移动的总步数为 T(n) = 2^0*(n-1) + 2^1*(n-2) + …… + 2^(h-2)*1
2T(n) = 2^1*(n-1) + 2^2*(n-1) + …… + 2^(h-2)*2 + 2^(h-1)*1
继续采用错位相减得到 T(n) = 2^h -1 -h = n - log(n+1) ≈ n
所以时间复杂度为 O(n) = n
排序:
我们采用向下调整这一思路,先对数组进行建堆,然后将堆的根节点与最后一个结点进行交换,最后对新的堆进行向下调整。
代码如下:
void HeapSort(int* a, int n) {
for (int i = (n - 1 - 1) / 2; i >= 0; i--) {
AdjustDown(a, n, i);
}//向下调整建立大堆
size_t end = n - 1;
while (end > 0) {
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
**算法复杂度分析:**建堆的算法复杂度为O(N),每次交换然后再进行向下调整的算法复杂度为O(NlogN),所以堆排序的算法复杂度为O(NlogN),由于直接在数组上进行操作空间复杂度为O(1)。
TOP-K问题
TOP-K问题就是在N个数中找到前K个数,一般来说TOP-K问题数据量较大穷举会浪费资源,我们可以采用堆排序的思想解决此问题。
算法思路:
- 用前K个数进行建堆。
- 然后将堆的根和剩下N-K个数比较,如果比根大的话就加入堆。
- 堆进行向下调整,保持堆属性。
void TOP_K(int* a, int n, int k) {
int* tmp = (int*)malloc(sizeof(int) * k);
assert(tmp);
for (int i = 0; i < k; ++i)
{
tmp[i] = a[i];
}
// 建小堆
for (int j = (k - 1 - 1) / 2; j >= 0; --j)
{
AdjustDown_S(tmp, k, j);
}
for (int i = k; i < n; i++) {
if (tmp[0] < a[i]) {
tmp[0] = a[i];
AdjustDown_S(tmp, k, 0);
}
}
for (int i = 0; i < k; i++) {
printf("%d ", tmp[i]);
}
printf("\n");
free(tmp);
}
每次建立的堆的最小进行比对,最后堆中剩下的就是前K个数据。
算法复杂度分析:
先对K个数建堆算法复杂度为O(K),然后N-K个数和根数据比较,然后恢复堆属性,最坏情况下算法复杂度为O(log(K)*(N-K)),所以最后整个算法的算法复杂度为O(K+log(K)(N-K)),空间复杂度为O(1),和建立大堆pop出K个数据相比较由于K一般比较小所以算法复杂度较低。
版权声明
本文为[风住尘香花已尽0.0]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_43710269/article/details/124063861
边栏推荐
猜你喜欢
随机推荐
MySQL存储引擎
Crawler example: crawl Taobao commodity information
对机器学习的理解
一个悄然崛起的国产软件
MySQL夺命16问,你能坚持到第几问?
Preliminary test of glusterfs storage setup
蓝牙协议栈的简单介绍
初窥 Pytest 测试框架,基础薄弱也能轻松 hold 住
pytorch机器学习之numpy基础
C语言每日一题——[NOIP2008]ISBN号码(牛客网第76题)
CEPH maintenance command understanding
MySQL storage engine
多线程之手动实现锁
录制你的第一个web 自动化测试用例
Definition of Derivative
Debian10搭建vsftpd服务器
How does redis query a key from massive data?
基本功:SQL 多表联合查询的几种方式
大学生职业发展与就业指导 中国大学mooc 福州大学 测验题目和答案
Wechat refund no approve protocol (protocol is disabled or cipher suits are inappropriate)









