C++数据结构:树(3.21)+堆(3.23)+topk问题(3.24)

目录

1.树的定义

2.二叉树

3.完全二叉树节点关系

4.满二叉树

5.堆

6.堆排序

7.堆排序的过程:排升序建立大堆

8.向上调整和向下调整

9.TopK问题



1.树的定义

左孩子右兄弟

树的应用 :文件夹系统

2.二叉树

节点的度<=2

满二叉树:每一个父亲节点都有两个孩子,最后的叶子节点全满

高度为k的满二叉树有:   2^k-1      个节点

完全二叉树:前k-1层是满二叉树,最后一层不满,但最后一层要求从左到右是连续的

满二叉树也是完全二叉树

 二叉树的一个结论:

 也就是任意的二叉树,度为0的节点比度为2的节点多一个。

3.完全二叉树节点关系

度为0 :  x个

度为1 : 1 或者0 个

度为2 :x-1个

 可以由节点数量范围算出来高度

4.满二叉树

满二叉树全是度为2 或者度为0的

5.堆

二叉树可以用数组存储

适合满二叉树或者完全二叉树

堆:

分为大根堆和小根堆,也就是堆是正金字塔或者倒金字塔。每一个父亲要么比孩子大要么比孩子小。堆构成的数组不一定有序

堆总是一颗完全二叉树

堆的作用,处理庞大数据topk问题,和堆排序

堆插入数据,先尾插,在判断时候需要调整,需要调整就调整。(调整一次,父亲变为孩子重新找父亲)一直调整到符合堆的规则

6.堆排序

1.把数组建立成堆

2.排升序 建立大堆,然后交换首位,size--就不会再访问到尾了。就放到那里了,然后在向下调整建立大堆,继续交换......

时间复杂度O(n*logn) 但是和O(n^2)差异很大。

7.堆排序的过程:排升序建立大堆

1.插入数据的时候建立堆AdjustUp(a,i)

2.利用堆的特性进行排序

3.大堆最大的在最上面,然后把根和最后一个交换。然后对第一个数据进行向下调整AdjustDown(DataType* a,int n, int parent)  这个向下调整可以控制调整的数组的大小。

这时候除了最后一个仍然是堆。然后继续循环上面的3

8.向上调整和向下调整

他们都有条件

向上调整的条件是:插入最后一个,前面已经是堆了

向下调整的条件是:左右子树都已经是堆

建立堆分为两种:

1.向上调整,模拟插入,向上调整前提是前面已经是堆

2.向下调整,前提是左右子树已经是堆

向下调整建堆比较好,时间效率高得多

向上调整时间复杂度为N*logN

向下调整得时间复杂度为N

大概理解一下,向下调整节点多的调整次数少,向上调整节点少的调整次数多

所以向下调整好,具体得证明可以用数学计算,错位相减

9.TopK问题

如何解决:假如要找100亿个数据中最大的50个

1.建立一个50个数据的小堆。

2.让后面的数据和堆顶元素比较,如果比堆顶大就交换,然后向下调整。

比较100亿次即可,之后就是最大的50个数

void adjustdown(int *a,int n,int parent)
{
	int child = parent * 2 + 1;//假设左孩子小
	while (child <n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])//注意条件没有child+1<n
		{
			int temp = a[child];
			a[child] = a[parent];
			a[parent] = temp;
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
		
	}

}
//找出最大的
int* Topk(int* a, int n, int k)
{
	// 1. 建堆--用a中前k个元素建小堆
	int* topkarr = (int*)malloc(sizeof(int) * k);
	memcpy(topkarr, a, sizeof(int) * k);
	for (int i = (k-2)/2; i>=0 ; i--)
	{
		adjustdown(topkarr, k, i);
	}
	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int l = k;
	while (n - k)
	{
		if (a[k] > topkarr[0])
		{
			topkarr[0] = a[k];
			adjustdown(topkarr, l, 0);
		}
		k++;
	}
	//for (int i = k; i < n; i++)
	//{
	//	if (a[i] > topkarr[0])
	//	{
	//		topkarr[0] = a[i];
	//		adjustdown(topkarr, k, 0);
	//	}
	//}
	return topkarr;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值