目录
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;
}