数据结构基本功——排序算法(堆排序)

既然要说堆排序,那么自然先问的是什么是堆?

       堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
        堆排序(Heap)是利用堆(假设利用大顶堆)进行排序的方法。

知道了堆和堆排序的概念之后,那么怎么实现堆排序呢?

目录

一、堆排序的基本思想是:

二、堆排序的C++实现


一、堆排序的基本思想是:

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

上面说了这么多,无非就是想解决两个问题:
(1)如何由无序序列构建成一个堆
(2)如何在输出堆顶元素以后,调整剩余元素称为一个新堆

(如果对理论不感兴趣的,可以直接看实现了 :) ^_^

说到这里,就不得不提起二叉树的往事了,二叉树呢,有这么一个性质:

       若完全二叉树中一共n个数,那么非根节点的个数一定等于 \left \lfloor \frac{n}{2}\right \rfloor 个,其中 \left \lfloor \right \rfloor 表示向下取整;
       因为,第i个结点的左孩子等于 2*i, 右孩子等于 2*i +1 (假设根节点是 i = 1); 通过对这  \left \lfloor \frac{n}{2}\right \rfloor 个结点分析大顶堆的构造,可以从序号最大的结点(层次最深的)\left \lfloor \frac{n}{2}\right \rfloor开始,然后,逐个往序号小的结点(层次较小的)0 构造。所以,此处构造堆只需要3个数之间,两次比较即可,构造耗时呢,也就2*\left \lfloor \frac{n}{2}\right \rfloor,即O(n)时间复杂度。最后,再经过n-1次正式排序,每次需要logn的复杂度。

因此,总得来说,堆排序的时间复杂度为O(nlogn),由于比较和交换是跳跃式进行的,因此堆排序也会是一种不稳定的排序方法。

厉害了,就这么一个性质,够用。

二、堆排序的C++实现

#include <iostream>
#include <algorithm>

using namespace std;

//在数组a[s...m]中,认为除了a[s]以外,其余结点满足大顶堆的条件
//通过调整a[s]的关键字,使得a[s...m]都满足大顶堆的条件
//下面是利用大顶堆实现顺序排序
void adjustheap(int *a, int s, int m)
{
	int temp = a[s];
	int j;
	for (j = 2 * s +1 ; j < m ; j = 2 * j + 1)
	{
		if (j+1 < m && a[j] < a[j+1])
			j++;
		if (a[j] <= temp)  //大顶堆已经确定,就是a[temp]
			break;
		a[s] = a[j];       //将较大值放在大顶堆上
		s = j;		       //沿着比较大的支流接着寻找
	}
	a[s] = temp;		   //插入到找到的位置
}

void heapsort(int *a, int length)
{
	int i;
	//1.大顶堆的构造
	for (int i = (length-1 - 1) / 2; i >= 0; i--)
		adjustheap(a, i, length);
	//2.输出堆顶元素,重新构造剩余的元素为大顶堆
	for (int i = length - 1; i > 0; i--)
	{
		swap(a[i], a[0]);      //第一个位置一定是大顶堆的堆顶
		adjustheap(a, 0, i);
	}
}

int main(int argc, char **argv)
{
	int a[] = { 1,3,5,6,7,2,10,8 };
	int b[] = { 90, 70, 80, 60 ,10 , 40, 50, 30, 20 };
	//swap(a[0], a[1]);
	heapsort(b, sizeof(b) / sizeof(int));
	for (int i = 0; i < sizeof(b) / sizeof(int); i++)
		cout << b[i] << " ";
	cin.get();
	return 0;
}

        其实,在上面的实现过程中,根节点是序号0,

1.如果根节点是i =0 开始的,那么满足下面的二叉树序号关系
                i
      2*i+1      2*i+2
此时,最大序号为n-1的二叉树,非根节点的个数就等于 \left \lfloor \frac{n}{2}\right \rfloor -1。
所以,在adjustheap中看到的也是 2*i+1和2*i+2的调整

2.如果根节点是i = 1开始的,那么满足下面的二叉树序号关系
               i
        2*i     2*i+1
此时,最大序号为n的二叉树,非根节点的个数就等于 \left \lfloor \frac{n}{2}\right \rfloor
这种情况下,a[0]往往作为前哨,待排序数组的元素从a[1]开始放置。那么,相应的,在adjustheap中看到的也是 2*i和2*i+1的调整。

最后,如果要实现逆序排序,那么可以用小顶堆实现。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值