【堆排序C/C++】


堆排序

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


一、堆是什么?

堆(heap):若n个关键字序列L[1…n]满足下面某一条性质,则称为堆:
.若满足L[i]>=L[2i]且L[i]>=L[2i+1] (1<=i<=n/2) ---------大根堆;
.若满足L[i]<=L[2i]且L[i]<=L[2i+1] (1<=i<=n/2) ---------小根堆;
在这里插入图片描述

二、堆排序基本原理

算法思想:
根据大顶堆的性质,每个节点的值都大于或者等于它的左右子节点的值。所以我们需要找到所有包含子节点的节点,也就是非叶子节点,然后调整他们的父子关系,非叶子节点遍历的顺序应该是从下往上,这比从上往下的顺序遍历次数少很多,因为,大顶堆的性质要求父节点的值要大于或者等于子节点的值,如果从上往下遍历,当某个节点既是父节点又是子节点并且它的子节点仍然有子节点的时候,因为子节点还没有遍历到,所以子节点不符合大顶堆性质,当子节点调整后,必然会影响其父节点需要二次调整。但是从下往上的方式不需要考虑父节点,因为当前节点调整完之后,当前节点必然比它的所有子节点都大,所以,只会影响到子节点二次调整。相比之下,从下往上的遍历方式比从上往下的方式少了父节点的二次调整。
那么,该如何知道最后一个非叶子节点的位置,也就是索引值?
对于一个完全二叉树,在填满的情况下(非叶子节点都有两个子节点),每一层的元素个数是上一层的二倍,根节点数量是1,所以最后一层的节点数量,一定是之前所有层节点总数+1,所以,我们能找到最后一层的第一个节点的索引,即节点总数/2(根节点索引为0),这也就是第一个叶子节点,所以第一个非叶子节点的索引就是第一个叶子结点的索引-1。那么对于填不满的二叉树呢?这个计算方式仍然适用,当我们从上往下,从左往右填充二叉树的过程中,第一个叶子节点,一定是序列长度/2,所以第一个非叶子节点的索引就是arr.length / 2 -1。
以大根堆为例
建立大根堆:检测所有非终端节点,不满足要求则进行调整(将当前节点与较大孩子节点互换)。
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

三、代码实现

#include <stdio.h>
#include<algorithm>
void HeadAdjust(int A[],int k,int len);
void BuildMaxHeap(int A[],int len)//建立大根堆
{
	for(int i = len/2;i > 0;--i)
	{
		HeadAdjust(A,i,len);
	}
 } 
 
void HeadAdjust(int A[],int k,int len)//元素下坠
{
	A[0]  = A[k];
	for(int i = 2*k;i <= len;i *= 2)
	{
		if(i < len && A[i]<A[i+1])
		i++;
		if(A[0] > A[i])
		break;
		else
		{
			A[k] = A[i];
			k = i;
		}
	}
	A[k] = A[0];
}
void HeapSort(int A[],int len)
{
	BuildMaxHeap(A,len);
	for(int i = len;i > 1;--i)
	{
		std::swap(A[i],A[1]);
		HeadAdjust(A,1,i-1);
	}
}
int main()
{
	  int arr[]={0,44,3,38,5,47,15,36,26,27,46,4,2,19,50,48};

  int len=sizeof(arr)/sizeof(int);

  HeapSort(arr,len-1);

  // 显示排序结果。
  int yy; for (yy=0;yy<len;yy++) printf("%2d ",arr[yy]); printf("\n");

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值