调建堆的时间复杂度的计算与topK问题

建堆复杂度的计算

向下调整建堆

第一层有2^0个节点,最坏向下调整h-1次,第二层有2^1个节点,最坏向下调整h-2次,以此类推,将每一层所有节点最坏情况需要调整的次数相加,就能得到一个式子:

T(h) = 2^0 *(h-1) + 2^1 * (h-2) + 2^2 * (h-3) + ... + 2^{h-3} * 2 +2^{h-2}*1

最后到达(n-1)层,有2^{h-2}个节点,最坏需要向下调整1次.

而要求出该和式的计算,利用错位相减法,两边同乘2,再两式相减即可得

T(h) = 2^h - 1 - h

而我们又知道对于高度为h的满二叉树的节点个数与高度的关系

N=F(h) = 2^h -1

带入原式可得

对于N来说,对数的数量级可以忽略不计.

所以向下调整建堆时间复杂度是O(N).

向上调整建堆

同样写出堆的总调整数公式

2^1*1 +2^2*2 +2^3*3+... + 2^{h-2}*(h-2)+ 2^{n-1}*(h-1)

同样错位相减并带入节点数与树高度之间关系可得

对于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检测算法是正确的.

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值