目录
1. 顺序存储
2.堆的实现:
节点结构:
堆的初始化:void HeapCreat(Heap* hp)
堆插入:void HeapPush(Heap* hp, HDType x)
堆删除:void HeapPop(Heap* hp)
取堆顶数据:HDType HeapTop(Heap* hp)
堆的数据的个数:int HeapSize(Heap* hp)
堆的判空:bool HeapEmpty(Heap* hp)
堆的销毁:void HeapDistroy(Heap* hp)
二叉树一般可以使用两种存储结构,一种是顺序结构,一种是链式结构。
今天我们一起看一看顺序结构。
1. 顺序存储顺序结构存储就是使用数组来存储,一般使用数值只适合表示完全二叉树,因为如果不是完全二叉树就会有空间的浪费。而在现实的使用中只有堆才会使用数组储存。二叉树顺序存储在物理上是一个数组,在逻辑上是一棵树。
可以看的在非完全二叉树的顺序结构中存在空间的浪费。
在数据结构中我们在堆(一种二叉树结构)中使用顺序结构的数组来储存。需要注意的是这里的堆是数据结构中的一种结构,与 *** 作系统中的堆不同。下面一起看看堆吧!!!
堆分为小根堆和大根堆;根节点始终小于子节点称为小根堆,相反根节点始终大于子节点则称为大根堆。
堆的性质:1.堆中某个节点的值总是不大于或者不小于其父节点的值
2.堆总是一颗完全二叉树。
2.堆的实现: 节点结构:因为是顺序结构,节点的结构和顺序表的一样。
typedef int HDType;
typedef struct Heap
{
int size;
int capacity;
HDType* data;
}Heap;
堆的初始化:void HeapCreat(Heap* hp)
堆的初始化就是将创建的堆的变量进行置空和置零
//堆的创建
void HeapCreat(Heap* hp)
{
assert(hp);
hp->data = NULL;
hp->capacity = 0;
hp->size = 0;
}
堆插入:void HeapPush(Heap* hp, HDType x)
因为堆在物理结构层面上数组,但是在逻辑层面上二叉树。而且只有小根堆和大根堆,所以插入数据之后要想保证任然是堆就要进行调整。插入时从尾部插入,而是否为堆取决于子节点和父节点的关系,所以插入进来数据要和它的父节点比较,小根堆时子节点要比父节点要大,否则的话就要交换子节点和父节点,大根堆相反;如图
这种调整方式叫做向上调整算法
代码:
//交换
void Swap(HDType* data1, HDType* data2)
{
HDType temp = *data1;
*data1 = *data2;
*data2 = temp;
}
//向上调整
void AdjustUp(HDType* data, int child)
{
HDType parent = (child - 1) / 2;
while (child > 0)
{
if (data[child] < data[parent])
{
Swap(&data[child], &data[parent]);
}
child = parent;
parent = (child - 1) / 2;
}
}
//堆的插入
void HeapPush(Heap* hp, HDType x)
{
assert(hp);
//创建并判断是否有剩余空间,若没有进行扩充
if (hp->capacity == hp->size)
{
int newcapacity = (hp->capacity == 0 ? 4 : 2 * hp->capacity);
HDType* New = (HDType*)realloc(hp->data, newcapacity * sizeof(HDType));
assert(New);
hp->data = New;
hp->capacity = newcapacity;
}
//在后面插入
hp->data[hp->size] = x;
hp->size++;
//向上调整
AdjustUp(hp->data,hp->size - 1);
}
堆删除:void HeapPop(Heap* hp)
堆删除是删除堆顶的元素,如果我们直接删除堆顶的元素,在将数据挪动,这样的话会破坏堆的结构,如图:
可以看出堆结构被破坏,所以这种方法并不可取;我们这里采用将堆顶的数据与最后一个数据交换,再删除最后一个数据,这样就做到了删除堆顶数据,接着我们在调整堆顶数据的位置,就好了
那该怎样调整呢? 将根节点与它的孩子中的较小值交换,然后再将交换后的节点作为父节点继续与它的子节点交换,直到该节点小于它的子节点,或者成为叶节点。使用这个方法有一个前提:根节点的两个子树也得是堆才行。这种方法叫做向下调整算法。如图:
代码:
//交换
void Swap(HDType* data1, HDType* data2)
{
HDType temp = *data1;
*data1 = *data2;
*data2 = temp;
}
//向下调整
void AdjustDown(HDType* data,int size,int parent)
{
int child = 2 * parent + 1;
while (child < size)
{
if (child + 1 < size && data[child + 1] < data[child])//注意边界
{
child++;
}
if (data[child] < data[parent])
{
Swap(&data[parent], &data[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
//堆的删除
void HeapPop(Heap* hp)
{
assert(hp);
Swap(&hp->data[0], &hp->data[hp->size - 1]);
hp->size--;
//向下调整
AdjustDown(hp->data, hp->size,0);
}
取堆顶数据:HDType HeapTop(Heap* hp)
直接返回堆顶的数据
//取堆顶元素
HDType HeapTop(Heap* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
return hp->data[0];
}
堆的数据的个数:int HeapSize(Heap* hp)
//堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->size;
}
堆的判空:bool HeapEmpty(Heap* hp)
//堆判空
bool HeapEmpty(Heap* hp)
{
assert(hp);
return hp->size == 0;
}
堆的销毁:void HeapDistroy(Heap* hp)
将前面开辟的空间进行释放,并置空,并且将容量和数据个数进行置零。
//堆销毁
void HeapDistroy(Heap* hp)
{
assert(hp);
free(hp->data);
hp->data = NULL;
hp->capacity = hp->size = 0;
}
到这里堆的接口就写完了;本篇的博客也就完了。
以上若有错误恳请各位前辈指正,感激不尽!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)