初阶数据结构完全二叉树实现堆排序

Posted 赏一杯茶:

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初阶数据结构完全二叉树实现堆排序相关的知识,希望对你有一定的参考价值。


前言

完全二叉树是效率很高的数据结构,用它进行排序算法时时间复杂度大大降低

一、完全二叉树的介绍

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

如上图示为满二叉树,深度K为3,结点n为7,即满二叉树的结点n与深度K存在关系为:n = 2^k - 1.

如上图示为完全二叉树,完全二叉树有如下性质:

1. K-1层全满,K层不满,即2^(K-1)<= n <= 2 ^(K) - 2;
2. 第K层结点从左至右连续,故度为1的结点(只有一个孩子的结点)不大于1个
3. n = n0(叶子结点) + n1(度为1的结点) + n2(度为2的结点),由于n1<=1,且n2 = n0 - 1

顺序存储二叉树:
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树.
上图二叉树物理表示为{0,1,2,3,4},{0,1,2,3,4,5}

二、堆向下调整算法

认识堆向下调整算法之前先了解下“大堆”与“小堆”

下图为小堆示例图,即父节点总小于子节点

此为大堆结构示例图,即父节点总大于子节点

顾名思义,向下调整算法即父节点与子节点比较调整

通过一个完全二叉树{27,15,19,18,28,34,65,49,25,37}了解向下调整算法

这是一个“伪小堆”,即除根结点外,根节点的左子树与右子树均满足小堆排序,将整体排为小堆后是{15,18,19,25,28,34,65,49,27,37},下面给出图解:



最终变成:

代码:

void Swap(int* a, int x, int y)
{
	int tmp = 0;
	tmp = a[x];
	a[x] = a[y];
	a[y] = tmp;
}
void Adjustdown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1< n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(a, child, parent);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

三、堆排序算法

有了向下调整算法后,可以尝试对任意数组排序,排序与大小堆刚好相反,排升序用大堆,排降序用小堆,现有数组{1,5,3,8,7,6},将其升序排列为{1,3,5,6,7,8};

排序先建堆,排升序建大堆

因为此时不再是除根结点外的堆结构,故应从最后一个叶子结点的父亲结点开始进行向下调整,图解如下:

而后对每一个父亲结点进行调整



以上为建堆过程,建堆好以后进行升序排列升序排列时不能破坏已经建好的堆的结构,即根结点不被破坏,所以可以对叶子结点进行调整

- 将根结点与最后一个叶子结点交换
- 除去最后一个叶子结点,用向下调整算法调整堆
- 直到最后一个单结点,排序完成

图示:


以此类推下去,选出{1,5,6,3}中的最大数6,接着选出{3,5,1}中的最大数5,其次是{1,3}中的最大数3,最后是1。
代码如下:

void Swap(int* a, int x, int y)
{
	int tmp = 0;
	tmp = a[x];
	a[x] = a[y];
	a[y] = tmp;
}
void Adjustdown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1< n && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(a, child, parent);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void Heapsort(int* a, int n)
{
	//升序用大堆,找到最大后与最后一个换一下,再调整
	//降序用小堆,找到最小后与最后一个换一下,再调整
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		Adjustdown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(a, end, 0);
		Adjustdown(a, end, 0);
		--end;
	}
}
int main()
{
	int a[] = { 1,5,3,8,7,6 };
	//Adjustdown(a , sizeof(a)/sizeof(int),0);
	Heapsort(a,sizeof(a)/sizeof(int));
	int i = 0;
	for (i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

以上是关于初阶数据结构完全二叉树实现堆排序的主要内容,如果未能解决你的问题,请参考以下文章

初阶数据结构完全二叉树实现堆排序

数据结构初阶第七篇——二叉树的顺序结构的应用(堆排序+TOPK问题)

[05数据结构](初阶)二叉树的基本理解堆的概念和结构及二叉树OJ面试题

初阶数据结构——初识二叉树及其应用——堆——及其向下向上调整算法

排序--堆排序分析与实现

java   堆排序 实现