八大排序之堆排序

堆排序引论:既然是堆排序,那么我们必须知道堆是什么?

堆是具有以下性质的完全二叉树:每个节点的值都大于等于左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。

根据堆的定义可知,根节点一定是堆中所有节点最大(小)着。较大(小)的节点靠近节点(但也不绝对)如图:

如果将上图按照层次遍历存入数组,那么一定满足下面的关系:

 堆排序算法

堆排序就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。

如图:

 

 知道了这个过程,那么我们现在主要需要解决两个问题:

  • 如何由一个无需队列构建成一个堆?
  • 如果在输出堆顶元素后,调整剩余元素成为一个新的堆?

在构建大顶推之前,我们再来看一下大顶堆和小顶堆的概念:每个节点的值都大于等于左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆

构建大顶堆时,我们从哪里开始构建呢?当然是先构建最小的大顶堆,即最后一个非叶子结点,如果此完全二叉树有n个元素的话,那么最后一个节点的就是第n/2-1个(完全二叉树的性质),此节点之前的元素都是非叶子节点,之后的元素都是叶子结点

那么构建大顶堆的过程就是这样滴(sort函数时具体的构建函数,稍后会实现):

	for (int i = len/2-1; i >=0; i--)
	{
		Sort(ar, i,len);
	}

构建完大顶堆之后,我们需要做的就是,交换元素,然后再构建大顶堆,以此类推,直到完全有序(因为我们每次交换元素之后,之前的根节点元素就不再参与构建大顶堆了,因此大顶堆的规模是逐渐减一的)。

那么过程就是这样滴:

	for (int i = len - 1; i > 0; i--)
	{
		int temp = ar[0];
		ar[0] = ar[i];
		ar[i] = temp;
		Sort(ar, 0, i);
	}

构建大顶堆的代码:

void Sort(int* ar, int i,int n)
{
	int tmp = ar[i];
	for(int k = i * 2 + 1; k < n; k = k * 2 + 1)
	{
		if (k + 1 < n && ar[k] < ar[k + 1])//判断左右孩子哪个大,ar[k]始终是较大的那个
			k++;
		if (ar[k] > tmp)
		{
			ar[i] = ar[k];
			i = k;
		}
		else
			break;
		ar[i] = tmp;
	}
}

完整图解:

 

 

 

 

 

完整图解:

void Sort(int* ar, int i,int n)
{
	int tmp = ar[i];
	for(int k = i * 2 + 1; k < n; k = k * 2 + 1)
	{
		if (k + 1 < n && ar[k] < ar[k + 1])
			k++;
		if (ar[k] > tmp)
		{
			ar[i] = ar[k];
			i = k;
		}
		else
			break;
		ar[i] = tmp;
	}
}
void HeapSort(int* ar, int len)
{
	assert(ar != NULL);
	for (int i = len/2-1; i >=0; i--)
	{
		Sort(ar, i,len);
	}
	for (int i = len - 1; i > 0; i--)
	{
		int temp = ar[0];
		ar[0] = ar[i];
		ar[i] = temp;
		Sort(ar, 0, i);
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值