【数据结构】二叉树中 堆的实现方法

目录

一. 树以及树相关的概念

二. 二叉树的概念以及结构;

三. 堆与二叉树:

1.什么是堆?与二叉树有什么关系?

2.堆的存储结构:

 3.堆的实现:

1. 创建结构体变量:

2. Push插入函数

3.Printf打印函数:

4.Pop删除函数:

5.其他简单函数:

6.检验案例:


一. 树以及树相关的概念

树是一种非线性的数据结构,它是由N个有限节点组成的具有层次关系的集合。同时树形结构中不能具有交集,否则就不是树形结构。

像这样下图的上三个都不能被称作树;

节点的度:一个节点含有的子树的个数称为该节点的度;

叶节点或终端节点:度为0的节点称为叶节点;

双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;

孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;

兄弟节点:具有相同父节点的节点互称为兄弟节点;

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

树的高度或深度:树中节点的最大层次;

森林:由M(M > 0)棵互不相交的树的集合称为森林;

二. 二叉树的概念以及结构;

一棵二叉树是结点的一个有限集合,该集合:
1. 为空;
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成;

 由图可以看出来:二叉树节点的度都不能大于 2;二叉树有左子树右子树之分,是有序树;

还有两个特殊的二叉树:

满二叉树:根据名字很容易理解,就是每个枝桠上都用两个分桠(最后一层除外)它的总节点数为 2^k - 1 K为层数。

完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于层数为K的,有n个结点的二叉树,当且仅当其每一个结点都与层数为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树,它的总结点数的范围为[2^(k-1) - 1 ,  2^k - 1]。

三. 堆与二叉树:

1.什么是堆?与二叉树有什么关系?

堆:有一组集合 的元素按照完全二叉树的顺序储存方式存储到一个一维数组中,根据存储的顺序不同分为小堆与大堆。

2.堆的存储结构:

堆 形象逻辑上的 结构是树形的 如下图的(a) 抽象的实际的储存结构是 以顺序表的形式 来存储的。

下标的表示方法 从上到下 从左到右依次变大;

任何一个孩子与双亲节点都有下面这种关系:

 3.堆的实现:

1. 创建结构体变量:

typedef struct Heap
{
	int* a;
	int capacity;
	int size;
}Heap;

2. Push插入函数

先来看 Push 函数; 我们的堆 当进行插入的时候还要必须保持堆的结构;当我们插入的数 并不符合 这个 堆的结构的时候 我们要思考如何将 新插入的数与原堆融为一体?我们需要将 不符合结构的这个数与它的双亲节点进行互换 就行了; 用代码来实现就是这样:

我们将向上调整的方法进行封装成函数,有了利于实现 低耦合 高内聚 的特点;

swap(int* x, int* y)
{
	assert(x && y);
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
void Adjustup(int* a, int size, int x)
{
	assert(a);
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}	
	}
}
void HeapPush(Heap* SL, int x)
{
	assert(SL);
	if (SL->capacity == SL->size)
	{
		int newcapacity = SL->capacity == 0 ? 4 : (SL->capacity * 2);
		int* newnode = (int*)realloc(SL->a, sizeof(int) * newcapacity);
		if (newnode == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		SL->capacity = newcapacity;
		SL->a = newnode;
	}
	SL->a[SL->size] = x;
	SL->size++;

	Adjustup(SL->a, SL->size, x);

}

3.Printf打印函数:

void HeapPrintf(Heap* SL)
{
	assert(SL);
	int i = 0;
	for (i = 0; i < SL->size; i++)
	{
		printf("%d ", SL->a[i]);
	}
	printf("\n");
}

4.Pop删除函数:

同样的 我们对堆 进行删除的时候 同样会破坏对的结构;有一种解决思路是这样的:

将 根节点 与 尾节点 进行互换 把Size减减;再重新向下调整更新一下堆, 把新的头调整到适当的位置。具体做法 我们来看代码的实现:

void AdjustDown(int* a,int size)
{
	assert(a);
	int parent = 0;
	int minchild = parent * 2 + 1;
	
	while (minchild < size - 1)
	{
		if (a[minchild + 1] < a[minchild])
		{
			minchild++;
		}
		if (a[parent] > a[minchild])
		{
			swap(&a[parent], &a[minchild]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* SL)
{
	assert(SL);
	if (SL->size == 0)
	{
		return;
	}
	swap(&SL->a[0], &SL->a[(SL->size) - 1]);
	SL->size--;
	AdjustDown(SL->a,SL->size);
}

5.其他简单函数:

bool HeapEmpty(Heap* SL)
{
	assert(SL);
	return SL->size == 0;
}

int HeapTop(Heap* SL)
{
	assert(SL);
	return SL->a[0];
}

void HeapDestory(Heap* SL)
{
	assert(SL);
	free(SL->a);
	SL->a = NULL;
	SL->capacity = SL->size = 0;
}

6.检验案例:

#include"标头.h"

test()
{
	Heap SL;
	HeapInit(&SL);
	HeapPush(&SL, 6);
	HeapPush(&SL, 7);
	HeapPush(&SL, 10);
	HeapPush(&SL, 23);
	HeapPush(&SL, 2);
	HeapPush(&SL, 35);
	HeapPrintf(&SL);
	HeapPop(&SL);
	HeapPrintf(&SL);
	int a = HeapEmpty(&SL);
	printf("%d\n", a);
	int b = HeapTop(&SL);
	printf("%d\n", b);
	HeapDestory(&SL);
}

int main()
{
	test();
	return 0;
}

运行结果:

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 27
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值