首页 > 其他分享 >数据结构(初阶5)---堆与堆排序(详解)

数据结构(初阶5)---堆与堆排序(详解)

时间:2024-11-16 10:49:40浏览次数:3  
标签:初阶 parent int 堆排序 --- HPDataType 二叉树 child 节点

堆与堆排序

再将堆排序之前,我们先引入二叉树概念

一.二叉树初探

1).基本概念

二叉树是一种数据结构,二叉树形如:
请添加图片描述

1.其中A节点被称为根节点,B和C对应的两个树分别被称为左子树右子树
2.度:节点上连接的子树的个数被称为这个节点的度,二叉树不存在度大于2的节点
3.度为0的节点被称为叶子节点。
4.该节点的孩子节点为该节点链接的下一个节点,如B的孩子节点为DE
5.该节点的父亲节点为该节点的上一个节点,如B的父亲节点为A
6.树的度:该数中所有节点的度的最大值。
其中二叉树还有两种特殊的结构:
7.树的层数,字面意思,根节点记为一层

2).满二叉树和完全二叉树

1.满二叉树:
若每一层的节点都是满的的情况,即层数为n,节点个数为2^n-1请添加图片描述

2.完全二叉树
当二叉树共有n层,其中n-1层是满二叉树,并且第n层的节点分布是从左到右依次排列的
请添加图片描述

1.度为0的节点的个数==度为2的节点的个数+1
2.有n个节点的满二叉树的高h=log2(n-1);

3.)二叉树的存储方式

在这里我们重点探究二叉树的顺序储存(ps:适用于满二叉树和完全二叉树)

请添加图片描述
将完全二叉树的每一层按顺序依次排列到数组中去,可以发现以下特点:
1.父亲节点的脚标n,儿子节点的脚标m,可以得出m=n * 2+1或m=n * -2+2,同理n=(m-1)/2
通过这种特性,我们可以用顺序结构去访问我们的完全二叉树

二.堆与堆排序

1.堆(完全二叉树的特例)

堆分为大堆和小堆:
大堆表示为一棵完全二叉树 ,其每一个父亲节点都比他的孩子节点要大
小堆表示为一棵完全二叉树 ,其每一个父亲节点都比他的孩子节点要小

1).建堆(向下调整法)

1.向下调整法:
请添加图片描述

typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = 0;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(HPDataType* a, int n, int root)
{
	int parent = root;
	int child =  root * 2 + 1;
	while (child<n)
	{
		if ((child+1)<n&&*(a + child) > *(a + child + 1))
		{
			child++;
		}
		if (*(a + child) < *(a + parent))
		{
			Swap(a + child, a + parent);
			parent = child;
			child = parent * 2 + 1;
			
		}
		else
		{
			break;
		}
	}
}

不难得出向下调整法的时间复杂度为O(logn)
** 2.建堆**
这是处理一个节点无序的情况,如果整个给的数组完全无顺,我们该怎么办呢
接下来,我们来讲一下建堆的过程:请添加图片描述
我们找到最后一个叶子节点M,可知M的父亲节点N是最后一个父亲节点,所以我们只需要从这个父亲节点开始向上依次遍历一遍,其中每一次遍历都在经历一次向下调整法,就可以实现建堆
eg:建小堆

typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = 0;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(HPDataType* a, int n, int root)
{
	int parent = root;
	int child =  root * 2 + 1;
	while (child<n)
	{
		if ((child+1)<n&&*(a + child) > *(a + child + 1))
		{
			child++;
		}
		if (*(a + child) < *(a + parent))
		{
			Swap(a + child, a + parent);
			parent = child;
			child = parent * 2 + 1;
			
		}
		else
		{
			break;
		}
	}
}
void HeapInit(Heap* php, HPDataType* a, int n)
{
	php->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	memcpy(php->_a, a, n*sizeof(HPDataType));
	php->_size = n;
	php->_capacity = n;
	int child_ini = (n - 1-1) / 2;
	while (child_ini>=0)
	{
		AdjustDown(php->_a, n, child_ini);
		child_ini--;
	}
int main()
{
	int a[] = { 27,15,22,33,44,23,43,2,32,12 };
	Heap HP;
	HeapInit(&HP, a, sizeof(a)/sizeof(a[0]));
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", HP._a[i]);
	}

	return 0;
}

我们来思考一下,建堆的时间复杂度O是多少
已知共有n个节点(假设为满二叉树)
树高h=log2(n+1)
每一层的个数=2^(h’-1)    h’为当前层数
遍历次数S=h*2^(0)+(h-1)*2 ^(1)+(h-2)2 ^(2)…+12 ^(h-1)
这是一个等差数列乘上等比数列,用错位相减法
2S-S最后的结果是

S=2^h -h -1=n-log2(n+1)
时间复杂度为O(n)

2).堆排序

接下来进入真正的重头戏------堆排序

如果我们随意给出一个数组,里面的数全是无序的
那么我们如何去排序呢,直接使用冒泡排序,选择排序,插入排序?
如果我们给的数组足够大,那么使用这种排序方法效率就会非常的低下

这时我们强大的堆排序就要派出用场了:
请添加图片描述

(ps:这边我们以升序为例,升序建大堆,降序建小堆)

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = 0;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(HPDataType* a, int n, int root)
{
	int parent = root;
	int child =  root * 2 + 1;
	while (child<n)
	{
		if ((child+1)<n&&*(a + child) < *(a + child + 1))
		{
			child++;
		}
		if (*(a + child) > *(a + parent))
		{
			Swap(a + child, a + parent);
			parent = child;
			child = parent * 2 + 1;
			
		}
		else
		{
			break;
		}
	}
}
void HeapSort(HPDataType* php, int n)
{
	int child_ini = (n - 1 - 1) / 2;
	while (child_ini >= 0)
	{
		AdjustDown(php, n, child_ini);
		child_ini--;
	}//先建堆
	Swap(php, php + n-1);
	n--;//第一次先交换
	for (;n>0;n--)
	{
		AdjustDown(php, n, 0);//每一次向下调整
		Swap(php, php + n - 1);
	}
}
nt main()
{
	int a[] = { 20,15,16,13,12,19 };
	HeapSort(a, sizeof(a) / sizeof(a[0]));
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

我们来思考一下,堆排序的时间复杂度是多少
第一次的建堆O(n)
往后的n次遍历,每一次都向下调整O(logn)
我们可以近似的去处理,时间复杂度为
O(n+nlogn)=O(nlogn)
可以看出这是一种极为高效的排列方式,
稳定:在最好和最坏的情况下时间复杂度都是O(nlogn)
不稳定:堆排序有可能会改变两个值相等的数的相对位置,这也是他的不稳定性

创作不易,恳请留赞ヾ(^^*)))
在这里插入图片描述

在这里插入图片描述

标签:初阶,parent,int,堆排序,---,HPDataType,二叉树,child,节点
From: https://blog.csdn.net/Mr_vantasy/article/details/143804684

相关文章