文章目录
前言
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。本文将要介绍的是二叉树的顺序存储结构。
1. 顺序结构
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费,完全二叉树更适合使用顺序结构存储。
现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
2. 实现顺序结构二叉树
一般堆使用顺序结构的数组来存储数据,堆是一种特殊的二叉树,具有二叉树的特性的同时,还具备其他的特性。
2.1 堆的概念与结构
如果有一个关键码的集合 K = { k 0 , k 1 , k 2 , . . . , k n − 1 } K = \{{k_0,k_1,k_2 , ...,k_{n−1}}\} K={k0,k1,k2,...,kn−1},把它的所有元素按完全二叉树的顺序存储方式存储,在一个一维数组中,并满足: K i < = K 2 ∗ i + 1 且 K i < = K 2 ∗ i + 2 ( K i > = K 2 ∗ i + 1 且 K i > = K 2 ∗ i + 2 ) K_i <= K_{2*i+1} 且K_i <=K_{2*i+2}(K_i >=K_{2*i+1}且K_i >= K_{2*i+2}) Ki<=K2∗i+1且Ki<=K2∗i+2(Ki>=K2∗i+1且Ki>=K2∗i+2),$i = 0、1、2… $,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。
2.2 堆的实现
堆底层结构为数组,因此定义堆的结构为:
typedef int HPDataType;
typedef struct Heap
{
HPDataType * a;
int size;
int capacity;
}HP;
//默认初始化堆
void HPInit(HP * php);
//利用给定数组初始化堆
void HPInitArray(HP * php, HPDataType * a, int n);
//堆的销毁
void HPDestroy(HP * php);
//堆的插入
void HPPush(HP * php, HPDataType x);
//堆的删除
HPDataType HPTop(HP * php);
// 删除堆顶的数据
void HPPop(HP * php);
// 判空
bool HPEmpty(HP * php);
//求size
int HPSize(HP * php);
//向上调整算法
void AdjustUp(HPDataType * a, int child);
//向下调整算法
void AdjustDown(HPDataType * a, int n, int parent);
上面这部分是堆实现所需要的一些方法,其中具体的方法由读者自己先来尝试实现,如有不会的可以在讨论区询问,将会由作者或者其它积极的读者来解答❤️❤️❤️
2.2.1 向上调整算法
将新数据插入到数组的尾上,再进行向上调整算法,直到满足堆。
代码实现:
//向上调整
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;
}
}
}
//插入操作,插入之后利用向上调整算法来保持堆的结构
void HPPush(HP * php, HPDataType x)
{
assert(php);
if (php->size == php->capacity)
{
size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType * tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
php->a = tmp;
php->capacity = newCapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);
}
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果)
由此可得:
2.2.2 向下调整算法
删除堆是删除堆顶的数据,将堆顶的数据和最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。
代码实现:
//向下调整算法
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;
}
}
}
//删除操作,删除之后利用向下调整算法来保持堆的结构
void HPPop(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);
}
由此可得:
3. 结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!