【数据结构】二叉树的存储结构——顺序结构及其堆的实现

【数据结构】二叉树的存储结构——顺序结构及其堆的实现,第1张

目录

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;
}

到这里堆的接口就写完了;本篇的博客也就完了。

以上若有错误恳请各位前辈指正,感激不尽!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1330679.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存