树
树是一种非线性的数据结构,它是一个由有限个结点组成的具有层次关系的集合。
因为其结构酷似一棵根朝上叶朝下的树,所以被命名为树
- 树有一个特殊的结点,被称为根结点,根结点是树中唯一没有前驱的结点。
- 除根结点以外其余结点又被分为N个互不相交的集合,每一个集合又可以被称为树。
- 树是递归定义的。
在树形结构中,子树之间不能有交集。
节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为4
叶节点或终端节点:度为0的节点称为叶节点; 如上图:C、F、G等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:B、D、E.等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为4
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为3
二叉树
树中特殊的结构。
- 每个结点至多有两个结点即二叉树不存在度大于二的结点。
- 二叉树的子树有左右之分,次序不能颠倒。二叉树是有序树。
二叉树中又有两种特殊的二叉树:
- 满二叉树:如果一个二叉树的每层结点都达到最大值,这个树就是满二叉树。
2. 完全二叉树:对于深度为h的有n个结点的二叉树,当且仅当其每一个结点都与深度h的满二叉树中编号从1-n的结点一一对应时被称为完全二叉树。
满二叉树是一种特殊的完全二叉树。
二叉树的性质:
- 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i-1)个结点.
- 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是 (2^h)-1.
- 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为 n2,则有 n0=n2 +1.
- 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log2(n+1).
- 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
- 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点 比特科技
- 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
- 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
堆
如果有一个完全二叉树按照严格的顺序从上到下依次排列则被称为堆。从根结点开始从大到小为大根堆,从小到大为小根堆。
堆中的某个结点的值总是不大于或不小于其父节点的值。 堆总是一棵完全二叉树。
堆的实现
-
堆向下调整算法
如果一个数组在逻辑上时一棵完全二叉树,且其根结点的左右子树都为堆才可以向下调整。
-
堆的创建
在创建堆时可以从最底层的结点开始逐层向下调整。
-
堆的具体实现
//调整函数
void swap(HPDataType* x1, HPDataType* x2){
HPDataType tmp = *x1;
*x1 = *x2;
*x2 = tmp;
}
//向上调整函数
void up(HPDataType* i, int n, int child){
int parent;
assert(i);
parent = (child - 1) / 2;
while (child > 0){
if (i[child] > i[parent]){
swap(&i[parent], &i[child]);
child = parent;
parent = (child - 1) / 2;
}
else{
break;
}
}
}
//向下调整函数
void down(HPDataType* i, int n, int parent){
int child = parent * 2 + 1;
assert(i);
while (child < n){
if (child + 1 < n&&child + 1 > child){
++child;
}
if (i[parent]>i[child]){
swap(&i[child], &i[parent]);
parent = child;
child = parent * 2 + 1;
}
else{
break;
}
}
}
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n){
int i;
assert(hp&&a);
hp->a = (HPDataType*)malloc(sizeof(HPDataType)*n);
hp->size = n;
hp->capacity = n;
for (i = 0; i < n; ++i){
hp->a[i] = a[i];
}
for (i = ( n - 2 ) / 2 ; i >= 0; --i){
down(hp->a, hp->size, i);
}
}
// 堆的销毁
void HeapDestory(Heap* hp){
assert(hp);
free(hp->a);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x){
assert(hp);
if (hp->size == hp->capacity){
hp->capacity *= 2;
hp->a = (HPDataType*)realloc(hp->a, sizeof(HPDataType)*hp->capacity);
}
hp->a[hp->size] = x;
hp->size++;
up(hp->a, hp->size, hp->size - 1);
}
// 堆的删除
void HeapPop(Heap* hp){
assert(hp);
swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
down(hp->a, hp->size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp){
assert(up);
return hp->a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp){
return hp->size;
}
// 堆的判空
int HeapEmpty(Heap* hp){
return hp->size == 0 ? 0 : 1;
}
//打印堆
void HeapPrint(Heap* hp){
int i;
for (i = 0; i < hp->size; ++i){
printf("%d ", hp->a[i]);
}
printf("\n");
}
// TopK问题:找出N个数里面最大/最小的前K个问题。
// 找最大的前K个,建立K个数的小堆
void PrintTopK(int* a, int n, int k){
Heap hp;
HeapCreate(&hp, a, k);
for (size_t i = k; i < n; ++i){
if (a[i]>HeapTop(&hp)){
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
for (int i = 0; i < k; ++i){
printf("%d", HeapTop(&hp));
HeapPop(&hp);
}
}
// 找最小的前K个,建立K个数的大堆
void PrintTopK(int* a, int n, int k){
Heap hp;
HeapCreate(&hp, a, k);
for (size_t i = k; k < n; ++i){
if (a[i] < HeapTop(&hp)){
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
for (int i = 0; i < k; ++i){
printf("%d ", HeapTop(&hp));
HeapPop(&hp);
}
}