其他分享
首页 > 其他分享> > 【八大排序】堆排序

【八大排序】堆排序

作者:互联网

系列文章目录

八大排序(一):直接插入排序

八大排序(二):Shell排序

八大排序(三):冒泡排序

八大排序(四):选择排序

八大排序(五):二路归并排序

八大排序(六):基数排序


目录

系列文章目录

文章目录

前言

1.概念提醒

完全二叉树

 大顶堆(小顶堆)

2.堆的数组表示及调整规则

数组表示

 调整规则

二、代码实现

1. 调整函数

2.从尾部向头部堆的调整

总结


前言

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆排序其实有些类似于选择排序,通过一次次调整,将待排序序列中最大值或者最小值轮换出来,再放入已排序序列中,但选择方法较为优化。

时间复杂度:O(nlogn)  

空间复杂度:O(1)  

稳定性:不稳定


一、排序规则

1.概念提醒

这里会涉及一些树和堆的简单用法,在此做出简单介绍,方便大家理解。

完全二叉树

二叉树:如下图所示,即为一个简单二叉树,有一个根节点,其中每个结点存在至多两个子节点。

 完全二叉树:简单理解,即是除最外层结点以外的所有结点位置都被占据,且最外层结点从左向右连续存在。

 大顶堆(小顶堆)

堆由完全二叉树构成,以数组表达的储存形式,其存在一定的排序概念,即大顶堆(根节点值大于子节点),小顶堆(根节点值小于子节点)。我们的堆排序是以大顶堆,即升序序列作为例子说明。

2.堆的数组表示及调整规则

数组表示

将数组中值与堆结构相对应,有如下规则:

下标为 i 结点的子节点为:2 * i + 1 与 2 * i + 2;

下标为 i 结点的父节点为: (i - 1) / 2;

假设我们有如下数组:

由上述规则可得,下标为0结点的子节点为1和2,1的父节点为0;

以此类推, 则它的堆结构图为:

 调整规则

首先,我们需要知道:大顶堆的根节点的值一定是所有值中最大的;

那我们根据这个信息来进行操作,即是每次将堆调整为大顶堆,然后再将值放入已排序序列;

1.从尾部到头部依次调整,因为数据刚开始是极为无序的状态,我们需要对每个堆进行大顶堆调整,使整个堆完成大顶堆的状态,并且只有从尾部开始调整,才能达到层层呈递的效果,即是越往上值越大。

 如上图所示,从尾部到头部每个堆进行调整,成为大顶堆;

最后获得一个完全的大顶堆,这时的根节点就是最大值。

2.将调整为根节点的最大值放在数组最后

并且这时根节点在数组下标为0的位置,将其与数组最后的位置的值进行交换即可,类似于选择排序(只是交换的位置不同),将其剔除待排序序列即可。

 重复上述1、2操作,直到这个堆只剩下一个结点。

注意、在重复1操作调整大顶堆的时候,只需要调整最大的堆即可,因为只有最大值和数组最后进行交换,而其他部分并没有变化。

二、代码实现

1. 调整函数

函数中先取出需要调整的堆的根结点值tmp,然后向下查找,与两子节点中较大值进行比较,若大于tmp,则放在当前根节点位置,并以当前下标作为新的根节点,并重复这个过程,找到值应该放置的位置。

2.从尾部向头部堆的调整

 以数组尾部下标为基准,找到其父结点下标,作为每次调整的根节点,然后向前遍历即可,堆的结束下标因为无法确定,我们直接用数组尾部下标即可。

void HeapAdjust(int arr[], int start, int end)
{
	int tmp = arr[start];
	for (int i = start * 2 + 1; i <= end; i = start * 2 + 1)//这里语句三是精髓,也就是为什么堆排序的时间复杂度是O(nlongn)的原因
	{
		if (i < end && arr[i] < arr[i + 1])//如果右孩子存在,且右孩子的值大于左孩子,则让i指向右孩子,不然就一直指向左孩子即可
		{
			i++;
		}
		//不管if 成功与否 此时i指向左右孩子中较大的那个孩子
		//接着让较大的孩子和tmp比较,如果大于tmp,则向上挪动
		if (arr[i] > tmp)
		{
			arr[start] = arr[i];
			start = i; //因为arr[i]的值向上方,则arr[i]就变成新的空白格子,则让start指向新的空白格子
		}
		else
		{
			break;//第二种跳出情况:空白位置左右孩子的值都小于tmp的孩子,则跳出循环,准备插入
		}
	}
	//此时,不管是通过for循环语句2(触底退出),还是通过break(左右孩子都小于tmp)
	//此时,则可以将tmp插入到空白格子
	arr[start] = tmp;
}
void HeapSort(int arr[], int len)
{
    assert(arr != NULL);
	//1.将其从内到外完整的调整一下(从数组最后一个堆开始)
	for (int i = (len - 1 - 1) / 2; i >= 0; i--)//最后一个节点的父节点就是我们需要的最后一个非叶子节点
	{
		HeapAdjust(arr, i, len - 1);//因为第三个值没有规律,所以统一写len-1,并且没有任何影响
	}


	//根节点的值和最后一个节点的值进行交换,然后将最后一个节点剔除掉
	for (int i = 0; i < len - 1; i++)//7个值 剔除6次即可   len个值 剔除len-1次  O(n)
	{
		int tmp = arr[0];
		arr[0] = arr[len - 1 - i];
		arr[len - 1 - i] = tmp;

		HeapAdjust(arr, 0, (len - 1 - i) - 1);//len-1-i 代表当前最后一个节点的下标,-1代表将其剔除循环
	}
}

总结

以上就是今天学习的内容,本文介绍了堆排序的排序规则和代码实现,堆排序速度较快,可用于排序算法的优化。

标签:tmp,arr,八大,堆排序,结点,len,排序,节点
来源: https://blog.csdn.net/W1024201/article/details/123610545