目录
一. 树以及树相关的概念
树是一种非线性的数据结构,它是由N个有限节点组成的具有层次关系的集合。同时树形结构中不能具有交集,否则就不是树形结构。
像这样下图的上三个都不能被称作树;
节点的度:一个节点含有的子树的个数称为该节点的度;
叶节点或终端节点:度为0的节点称为叶节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
森林:由M(M > 0)棵互不相交的树的集合称为森林;
二. 二叉树的概念以及结构;
一棵二叉树是结点的一个有限集合,该集合:
1. 为空;
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成;
由图可以看出来:二叉树节点的度都不能大于 2;二叉树有左子树右子树之分,是有序树;
还有两个特殊的二叉树:
满二叉树:根据名字很容易理解,就是每个枝桠上都用两个分桠(最后一层除外)它的总节点数为 2^k - 1 K为层数。
完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于层数为K的,有n个结点的二叉树,当且仅当其每一个结点都与层数为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树,它的总结点数的范围为[2^(k-1) - 1 , 2^k - 1]。
三. 堆与二叉树:
1.什么是堆?与二叉树有什么关系?
堆:有一组集合 的元素按照完全二叉树的顺序储存方式存储到一个一维数组中,根据存储的顺序不同分为小堆与大堆。
2.堆的存储结构:
堆 形象逻辑上的 结构是树形的 如下图的(a) 抽象的实际的储存结构是 以顺序表的形式 来存储的。
下标的表示方法 从上到下 从左到右依次变大;
任何一个孩子与双亲节点都有下面这种关系:
3.堆的实现:
1. 创建结构体变量:
typedef struct Heap
{
int* a;
int capacity;
int size;
}Heap;
2. Push插入函数
先来看 Push 函数; 我们的堆 当进行插入的时候还要必须保持堆的结构;当我们插入的数 并不符合 这个 堆的结构的时候 我们要思考如何将 新插入的数与原堆融为一体?我们需要将 不符合结构的这个数与它的双亲节点进行互换 就行了; 用代码来实现就是这样:
我们将向上调整的方法进行封装成函数,有了利于实现 低耦合 高内聚 的特点;
swap(int* x, int* y)
{
assert(x && y);
int tmp = *x;
*x = *y;
*y = tmp;
}
void Adjustup(int* a, int size, int x)
{
assert(a);
int child = size - 1;
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])
{
swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(Heap* SL, int x)
{
assert(SL);
if (SL->capacity == SL->size)
{
int newcapacity = SL->capacity == 0 ? 4 : (SL->capacity * 2);
int* newnode = (int*)realloc(SL->a, sizeof(int) * newcapacity);
if (newnode == NULL)
{
perror("realloc fail");
exit(-1);
}
SL->capacity = newcapacity;
SL->a = newnode;
}
SL->a[SL->size] = x;
SL->size++;
Adjustup(SL->a, SL->size, x);
}
3.Printf打印函数:
void HeapPrintf(Heap* SL)
{
assert(SL);
int i = 0;
for (i = 0; i < SL->size; i++)
{
printf("%d ", SL->a[i]);
}
printf("\n");
}
4.Pop删除函数:
同样的 我们对堆 进行删除的时候 同样会破坏对的结构;有一种解决思路是这样的:
将 根节点 与 尾节点 进行互换 把Size减减;再重新向下调整更新一下堆, 把新的头调整到适当的位置。具体做法 我们来看代码的实现:
void AdjustDown(int* a,int size)
{
assert(a);
int parent = 0;
int minchild = parent * 2 + 1;
while (minchild < size - 1)
{
if (a[minchild + 1] < a[minchild])
{
minchild++;
}
if (a[parent] > a[minchild])
{
swap(&a[parent], &a[minchild]);
parent = minchild;
minchild = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapPop(Heap* SL)
{
assert(SL);
if (SL->size == 0)
{
return;
}
swap(&SL->a[0], &SL->a[(SL->size) - 1]);
SL->size--;
AdjustDown(SL->a,SL->size);
}
5.其他简单函数:
bool HeapEmpty(Heap* SL)
{
assert(SL);
return SL->size == 0;
}
int HeapTop(Heap* SL)
{
assert(SL);
return SL->a[0];
}
void HeapDestory(Heap* SL)
{
assert(SL);
free(SL->a);
SL->a = NULL;
SL->capacity = SL->size = 0;
}
6.检验案例:
#include"标头.h"
test()
{
Heap SL;
HeapInit(&SL);
HeapPush(&SL, 6);
HeapPush(&SL, 7);
HeapPush(&SL, 10);
HeapPush(&SL, 23);
HeapPush(&SL, 2);
HeapPush(&SL, 35);
HeapPrintf(&SL);
HeapPop(&SL);
HeapPrintf(&SL);
int a = HeapEmpty(&SL);
printf("%d\n", a);
int b = HeapTop(&SL);
printf("%d\n", b);
HeapDestory(&SL);
}
int main()
{
test();
return 0;
}
运行结果: