建堆复杂度的计算
向下调整建堆
第一层有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检测算法是正确的.