ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【八大排序】堆排序

2022-03-20 22:31:53  阅读:177  来源: 互联网

标签:tmp arr 八大 堆排序 结点 len 排序 节点


系列文章目录

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

八大排序(二):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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有