建堆复杂度的计算
向下调整建堆
第一层有2^0个节点,最坏向下调整h-1次,第二层有2^1个节点,最坏向下调整h-2次,以此类推,将每一层所有节点最坏情况需要调整的次数相加,就能得到一个式子:
最后到达(n-1)层,有个节点,最坏需要向下调整1次.
而要求出该和式的计算,利用错位相减法,两边同乘2,再两式相减即可得
而我们又知道对于高度为h的满二叉树的节点个数与高度的关系
带入原式可得
对于N来说,对数的数量级可以忽略不计.
所以向下调整建堆时间复杂度是O(N).
向上调整建堆
同样写出堆的总调整数公式
同样错位相减并带入节点数与树高度之间关系可得
对于nlogn来说,n的数量级可以忽略不计.
向上调整建堆时间复杂度是O(NlogN).
故在堆排序中只用向下调整建堆即可.
topk问题
在所有数据中找到最大(小)的k个数.
1.堆排序
将所有数据存储到堆里面,再进行堆排序即可,但如果要求低内存我们就可以分k次,每次找10个,再将k个查找的堆中找到的共10k个数据再进行堆排序即可。代码如下:
void TestHeap1()
{
int a[] = { 4,2,8,1,5,6,9,7,3,2,23,55,232,66,222,33,7,1,66,3333,999, 15155 };
HP hp;
HPInit(&hp);
for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
{
HPPush(&hp, a[i]);
}
printf("\n");*/
//找出最大的前k个
int k = 0;
scanf("%d", &k);
while (k--)
{
printf("%d ", HPTop(&hp));
HPPop(&hp);
}
printf("\n");
HPDestroy(&hp);
}
2.建k个数的小堆
剩下的数与栈顶数据相比较,如果比栈顶数据大,就覆盖并且向下调整,这样,这个k个数的小堆就是我们所要的最大的那k个数.
先创建数据
void CreateNDate()
{
// 造数据
int n = 100000;
srand(time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{
perror("fopen error");
return;
}
for (int i = 0; i < n; ++i)
{
int x = (rand() + i) % 10000000;//保证其小于一千万
fprintf(fin, "%d\n", x);//将数据导入data.txt中
}
fclose(fin);
}
创建一个数组
int* kminheap = (int*)malloc(sizeof(int) * k);
if (kminheap == NULL)
{
perror("malloc fail!");
return;
}
再打开数据文件并读取其中的前k个数
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (fout == NULL)
{
perror("fopen error");
return;
}
// 读取文件中前k个数
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &kminheap[i]);
}
再建立k个数的小堆,并读取剩下的N-k个数.
// 建K个数的小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)//最后一个节点的下标减一再除以二得到最后一个非叶子节点
{
AdjustDown(kminheap, k, i);
}
// 读取剩下的N-K个数
int x = 0;
while (fscanf(fout, "%d", &x) > 0)//fscanf失败返回-1,所以这里我们让其大于0即可
{
if (x > kminheap[0])
{
kminheap[0] = x;//将此数据与堆顶数据交换
AdjustDown(kminheap, k, 0);
}
}
printf("最大前%d个数:", k);
for (int i = 0; i < k; i++)
{
printf("%d ", kminheap[i]);//此时堆里数据就是最大的前k个
}
printf("\n");
整合起来
void TestHeap3()
{
int k;
printf("请输入k>:");
scanf("%d", &k);
int* kminheap = (int*)malloc(sizeof(int) * k);
if (kminheap == NULL)
{
perror("malloc fail");
return;
}
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (fout == NULL)
{
perror("fopen error");
return;
}
// 读取文件中前k个数
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &kminheap[i]);
}
// 建K个数的小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)//最后一个节点的下标减一再除以二得到最后一个非叶子节点
{
AdjustDown(kminheap, k, i);
}
// 读取剩下的N-K个数
int x = 0;
while (fscanf(fout, "%d", &x) > 0)
{
if (x > kminheap[0])
{
kminheap[0] = x;
AdjustDown(kminheap, k, 0);
}
}
printf("最大前%d个数:", k);
for (int i = k - 1; i >= 0; i--)//最后将数组数据倒序打印出来就可以达成数据由大到小的打印了
{
printf("%d ", kminheap[i]);
}
printf("\n");
}
这里我们创建了10万个数据,并且我主动将其中的几个数据加上几个0检测算法是正确的.