二叉树和堆(1)——堆的实现

树的概念和结构

树的概念

树是一种非线性的数据结构,它是由n个有限节点组成的一个具有层次关系的集合,把它叫做树是因为它看起来像一棵倒挂的树。

树的结构

下面介绍一些树比较常用的概念。

节点的度:一个节点含有的字数的个数称为该节点的度。比如A的度是3

终端节点:度为0的节点。C、D、E、F、G都是终端节点

非终端节点:度不为0的节点。比如A和B节点。

父节点:有一个节点有子节点,则这个节点是子节点的父节点。A是B、C、D的父节点

子节点:一个节点含有的子树的节点。B是A的子节点。

兄弟节点:具有相同父亲节点的节点互称为子节点。B和C互为子节点。

树的度:一棵树中,最大的度就是该树的度。

树的高度(或深度):树中节点的最大层次。上图树的高度是3。

节点的祖先:从根引出来的所有节点,根就是其祖先。A是所有节点的祖先。

二叉树的概念和结构

二叉树的概念

每个节点的度小于等于2的树。即一棵二叉树是节点的一个有限集合,该集合为空或者有一个根节点加上两棵称为左子树和右子树的二叉树组成。

上图的都为二叉树。

特殊的二叉树:

满二叉树:一个二叉树,如果每层的节点都达到最大值,这个二叉树就是满二叉树。即一个二叉树的曾处为k,且节点总数为2^K-1,它就是一个满二叉树。

完全二叉树:完全二叉树是由满二叉树引出来的。对于深度为k的,有n个节点的二叉树,当且仅当其中每一个节点斗鱼深度为k的满二叉树中编号从1至n的节点一 一对应时,称为完全二叉树。满二叉树时一种特殊的完全二叉树。

二叉树的结构

对于一个有n个节点的完全二叉树,如果按照从下至上,从左到右的数组顺序对所有节点从0开始编号,则对于序号为i的节点:

若i>0,i位置的父节点序号:(i-1)/2;

若2i+1<n,左孩子序号:2i+1,2i+1>=n,否则无左孩子:

若2i+2<n,右孩子序号:2i+2,2i+2>=n,否则无右孩子:

二叉树一般可以使用两种数据结构存储,一种是顺序结构,一种是链式结构。

顺序结构

一般只适用于表示完全二叉树,因为不会有空间浪费。现实使用中只有堆才会使用数组来存储。二叉树顺序结构存储物理上是数组,逻辑上是一棵二叉树。

链式结构

用链来表示一棵二叉树。通常的方法时链表中的每个节点由三个域组成,数据域和左右指针,左右指针用来给出节点左孩子和右孩子的地址。链式结构分为二叉链和三叉链。

二叉树的顺序结构与实现

现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储。需要注意的是这里的堆和操作系统虚拟进程地址空间的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

如果一个数组,把数组里面的数据按照完全二叉树的顺序存储方式存储在一个一维数组中,这种数据结构就是堆。堆分为大堆和小堆。

大堆:父节点的数据大于子节点的数据

小堆:父节点的数据小于子节点的数据

堆的实现

在实现堆前,我们先看一个例子,以大堆举例。

向上调整算法:

如果我们插入一个98,98比96大,这样插入98后,就不是一个大堆了。我们就要向上调整。把98和96换一下位置,让98当父亲,96当儿子(这点是不是有点像男生之间的父子情bushi

向下调整算法:

如果我们想要实现删除堆的数据,由于堆插入数据时进行了调整,那我们该怎么删除数据呢?我们就是把根和最后一个数据交换位置,在删除原来根的数值。

可是这样做之后,就不是堆了,我们就要调整,也就是向下调整。向下调整的原理是什么呢?原理就是让根和两个儿子中最大的那个比较,如果根小于儿子,就交换,以此类推,直到变成堆为止。

这就是向下调整算法。

闲话不多说,我们开始实现堆。

代码如下:

void HeapInit(HP* php)
{
	php->a = NULL;
	php->capacity = php->size = 0;
}
void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
void swap(int* p, int* q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;
}
void Adjustup(int* arr, int child)//向上调整
{
	//int parent = (child - 1) / 2;
	while (child>0)
	{
		int parent = (child - 1) / 2;
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = (child - 1) / 2;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
		//child = (child - 1) / 2;
		//parent = (parent - 1) / 2;
	}
}

void HeapPush(HP* php, int x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		php->capacity = newcapacity;
		int* tmp = (int*)realloc(php->a, sizeof(int) * php->capacity);
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		php->a = tmp;
	}
	php->a[php->size] = x;
	php->size++;
	Adjustup(php->a,php->size-1);//向上调整
}
Adjustdown(int* arr, int size, int parent)//向下调整算法
{
	//设左孩子,假设左孩子最小
	int child = parent * 2 + 1;
	while (child<size)
	{
		if (child+1<size&&(arr[child] > arr[child + 1]))
		{
			child++;
		}
		if (arr[parent] < arr[child])
		{
			swap(&arr[parent], &arr[child]);
			//swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
	
}

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size);
	swap(&php->a[php->size - 1], &php->a[0]);
	php->size--;
	Adjustdown(php->a, php->size, 0);//向下调整
}
int HeapTop(HP* php)
{	
	assert(php);
	assert(php->size);
	return php->a[0];
}
int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size;
}

这里鼠鼠实现的是大堆,大家可以实现以下小堆!

好的,今天就到这里了捏!

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值