数据结构-二叉树-堆

一、物理结构和逻辑结构

在内存中的存储结构,逻辑结构为想象出来的存储结构。

二、完全二叉树的顺序存储结构

parent = (child - 1)/2

leftchild = 2*parent + 1;

rightchild = 2*parent +2

上面的顺序结构只适合存储完全二叉树。如果存储,会浪费很多的空间。

三、堆

1、堆的分类

小根堆:树中所有的父亲都小于或等于孩子。

大根堆:树中所有的父亲都大于或等于孩子。

接下来我们需要定义一个堆。定义过程如下:

创建堆的时候会涉及到一个向上调整的算法:我们可以画图表示这一过程

cpp 复制代码
void UpAdjust(HPDataType* a, int child)
{
	assert(a);
	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;
		}
	}
}

删除堆顶数据需要涉及到向下调整

这里不能挪动数据。原因有两个,效率低下,父子兄弟关系全乱了。

思路就是:把头部数据和尾部数据交换。删除尾部数据,然后进行向下调整

这样删除的优点是 效率高,保持了大部分堆的父子关系。

向下调整的过程中我们需要和儿子中最大的进行比较。这样才能保证堆的关系不变。

没有孩子时结束,转换一下就是child < size。

cpp 复制代码
void DownAdjust(HPDataType* a,int parent,int size)
{
	int child = 2 * parent + 1;

	while (child < size)
	{	
		//选出最大的那一个孩子
		if (child + 1 < size && a[child] < a[child + 1])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			//交换两个数字
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

整体实现

cpp 复制代码
#include "heap.h"

void HeapInit(HP* hp)
{
	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * SIZE);
	hp->size = 0;
	hp->capacity = SIZE;
}

void AddCapacity(HP* hp)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		HPDataType* temp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * hp->capacity * 2);
		if (temp == NULL)
		{
			perror("realloc failed");
			return;
		}
		hp->a = temp;
		hp->capacity *= 2;
	}
}
void Swap(int* left, int* right)
{
	int temp = *left;
	*left = *right;
	*right = temp;
}
//除了child的位置,前面的数据构成堆
void UpAdjust(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 = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(HP* hp, HPDataType x)
{
	//考虑扩容的问题
	AddCapacity(hp);
	//插入数据
	hp->a[hp->size++] = x;
	//还需要考虑向上调整的问题。
	UpAdjust(hp->a, hp->size - 1);
}

void DownAdjust(HPDataType* a,int parent,int size)
{
	int child = 2 * parent + 1;

	while (child < size)
	{	
		//选出最大的那一个孩子
		if (child + 1 < size && a[child] < a[child + 1])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			//交换两个数字
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
//删除头部的数据
void HeapPop(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	//首先交换头和尾的数字
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	//然后删除尾的数字
	hp->size--;
	//向下调整恢复堆的原型 向下调整的左右子树一定是堆
	DownAdjust(hp->a, 0, hp->size);
}

HPDataType HeapTop(HP* hp)
{
	assert(hp);
	return hp->a[0];
}
bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}
int HeapSize(HP* hp)
{
	assert(hp);
	return hp->size;
}

四、堆排

对数组进行排序。我们可以把它直接搞成一个堆,建堆操作

1、向上调整建堆

(1)把第一个数看成一个堆中的数。后来的数进行向上调整建立堆。 O(nlogn)

(2)排升序需要建大堆,减小堆关系就都乱了

利用向上调整建大堆时我们可以交换堆头和堆尾的值。然后在进行向下调整选出次小的值,如此往复。

堆排的过程如下

堆排代码:

cpp 复制代码
void HeapSort(int* a,int n)
{
	//首先建立大堆
	for (int i = 1; i < n; i++)
	{
		UpAdjust(a, i);
	}

	//交换堆头和堆尾的数字选出最大的数字放到堆尾
	//然后向下调整
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		DownAdjust(a, 0, end);
		end--;
	}
}
相关推荐
汀、人工智能3 小时前
[特殊字符] 第40课:二叉树最大深度
数据结构·算法·数据库架构·图论·bfs·二叉树最大深度
赫瑞4 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
大熊背4 小时前
ISP Pipeline中Lv实现方式探究之三--lv计算定点实现
数据结构·算法·自动曝光·lv·isppipeline
yuannl108 小时前
数据结构----队列的实现
数据结构
Mr_Xuhhh8 小时前
从ArrayList到LinkedList:理解链表,掌握Java集合的另一种选择
java·数据结构·链表
Ricardo-Yang10 小时前
SCNP语义分割边缘logits策略
数据结构·人工智能·python·深度学习·算法
soragui10 小时前
【Python】第 4 章:Python 数据结构实现
数据结构·windows·python
samroom11 小时前
【鸿蒙应用开发 Dev ECO Studio 5.0版本】从0到1!从无到有!最全!计算器------按钮动画、滑动退格、中缀表达式转后缀表达式、UI设计
数据结构·ui·华为·typescript·harmonyos·鸿蒙
算法鑫探11 小时前
10个数下标排序:最大值、最小值与平均值(下)
c语言·数据结构·算法·排序算法·新人首发
王老师青少年编程12 小时前
csp信奥赛c++之状压枚举
数据结构·c++·算法·csp·信奥赛·csp-s·状压枚举