一、二叉树的概念及结构
1.1二叉树
1.2特殊的二叉树
1.3二叉树的性质
1.4二叉树的存储结构
二、堆的结构及性质
2.1堆的概念及结构
2.2堆向下调整算法
三、堆的代码实现及应用
//初始化节点
void HeapInit(HP* php);
//向上调整
void AdjustUp(HPDataType* a, int child);
//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//插入数据
void HeapPush(HP* php, HPDataType x);
//删除堆
void HeapDestroy(HP* php);
//删除堆顶数据
void HeaPop(HP* php);
//返回堆顶数据
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
//打印数据
void HeapPrint(HP* php);
我们将根据以上声明来实现堆的增、删、查、改以及向上和向下调整。
3.1堆的创建及初始化
typedef int HPDataType;
//创建堆
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
//初始化节点
void HeapInit(HP* php)
{
assert(php);
php->a = NULL;
php->size = 0;//元素个数
php->capacity = 0;//容量
}
首先创建一个名为Heap即堆的结构体,包含一个数组int a[]用来存放堆所存储的数据,int size来记录目前堆顶所在位置,int capacity用来显示堆的容量,便于在之后push数据过程中与size进行实时比较,从而判断堆容量是否足够用来继续存放数据,如果不够,进行动态内存开辟来开辟空间。至此,堆的创建进行完毕。
将所建堆的地址作为HeapInit(HP* php)的参数进行传参,进入函数内部,先使用assert对php进行判空断言,如果为空则证明该堆不存在,向编译器进行报错。将堆内部所创建的数组地址滞空为NULL,int size和int top初始化为0。至此,初始化结束。
3.2向上调整
//向上调整
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])//大堆改>
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (parent - 1) / 2;
}
else
{
break;
}
}
}
向上与向下调整是在堆push和delete过程中时堆内部的数据调整与排列。是堆的实现及应用中不可缺少的一部分。
堆在物理结构上作为一个数组,其在逻辑结构上作为一颗完全二叉树,其根节点所表示的元素下标为0,叶子节点和中间节点的下标随着树的深度的增加而变大。
向上调整顾名思义就是根据大堆小堆来将大的或小的数据向上调整,以此来保持堆这个概念。
将堆的数组地址当做参数进行传参,将需要调整元素的下标作为child子节点进行传参。进入函数内部,先根据child计算出其parent父节点,因为堆作为一颗完全二叉树,每个父节点最多只能有两个子节点,而二叉树根节点下标为0,根据观察我们可以得出,左child=parent*2+1;右child=parent*2+2;而要通过child求parent,左child直接除2可以得到parent还余1,而右child除2得到的是parent+1;所以右child想要得到parent需要先进行-1再除2;而左child-1除二同样可以得到parent。所以在计算parent值时,直接拿(child-1)/2;
进入while循环,根据大小堆判断child是否小于或大于parent,如果不符合,就交换child和parent的值,然后child和parent继续向上走和上面的数据进行比较,直到符合堆的结构,最坏情况下,换到最后走到根后跳出循环。如果数本身所在位置就符合堆的结构就直接跳出循环。此次循环结束。
3.3向下调整
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
//找出最小的孩子
if ((child + 1 < n) && (a[child + 1] < a[child]))//大堆改>
{
++child;
}
if (a[child] < a[parent])//大堆改>
{
Swap(&a[child], &a[parent]);
//继续往下调整
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
向下调整所传参数和向上调整一样,唯一不同是将所传下标对应的值当做父节点向下调整。进入函数内部,通过父节点下标求出子节点下标,准确来说叫左子节点下标,进入while循环,首先判断是否只有一个叶子节点,如果只有一个叶子节点就直接与parent进行比较来判断是否需要交换。如果有两个子节点,需要先判断左子节点和右子节点的大小关系。因为堆的结构具有特殊性,大小堆的父节点比两个子节点都大或者都小,但两个子节点之间并没有直接的大小关系。所以在向下调整时必须找出父节点和两个子节点之间最大或最小的数。
拿小堆举例,如果右子节点比左子节点大,child就++指向右子节点,反之则不动,此时child指向的节点就是最小的节点。然后比较与parent的大小,如果比parent就交换如果大就break。大堆两个子节点和与父节点的比较符号与小堆刚好相反。
3.4插入数据
//插入数据
void HeapPush(HP* php, HPDataType x)
{
assert(php);
if (php->size == php->capacity)
{
int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
//扩容
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
php->a = tmp;
php->capacity = newCapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);
}
将堆的地址和需要插入的数据当做参数进行传参。
进入函数先对php进行assert进行断言判空。判断size(目前下标所指位置)和capacity(堆的容量)的大小。如果size>capacity,则说明目前堆的容量已经满了需要进行扩容处理。创建一个变量newcapacity来表示扩容后堆的容量。判断此时堆的容量,如果为0则证明此时堆中没有数据,newcapacity就赋值为4,如果有值就赋值比原来大一倍的数即开辟比原来大一倍的容量空间。
接下来创建一个tmp的变量来存放realloc(扩容)出的空间地址。realloc出newcapacity个int大小的空间。并判断是否开辟成功,如果没有成功就perror出错误信息。开辟成功后,将x的值赋给a[size],然后size++为下一次插入做准备。因为此处插入是在数组最后进行插入,即堆的叶子节点的位置进行插入,所以此时就需要向上调整,将数据调整到适合它的位置。
3.5删除堆顶数据
void HeaPop(HP* php)
{
assert(php);
assert(php->size > 0);
Swap(&php->a[0], &php->a[php->size - 1]);//箭头优先级更高
--php->size;
AdjustDown(php->a, php->size, 0);
}
将堆顶数据先与最后一个叶子节点的值进行交换,然后--size将size指向原本最后一个数据,这样就可以在下次插入时直接覆盖掉,避免了多余重复的删除操作。接下来把刚刚换到堆顶的数据进行向下调整,调整到合适的位置。
3.6删除堆
void HeapDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
}
将堆中的数组a free掉并滞空。size和capacity都归零。
3.7返回堆顶数据
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
将a[0]返回。
3.8判空
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}
函数类型为bool型,assert断言php,如果size==0,return 为空。