排序算法---堆排序

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最小堆:

由于其它几种堆(二项式堆,斐波纳契堆等)用的较少,一般将二叉堆就简称为堆。

堆得存储

一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。


堆化数组


重上图看: 叶子结点 20 ,60 ,65 ,4 ,19都可以认为是一个合法的堆。

现在只要充A[4] = 50 向下调整。然后再A[3]=30,A[2] = 17,A[1] = 12,A[0] = 9分别作一次向下调整操作就可以了


初始堆建立完毕,然后就是排序。


堆排序

首先可以看到堆建好之后堆中第0个数据是堆中最小的数据。取出这个数据再执行下堆的删除操作。这样堆中第0个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。类似于选择排序。

第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。

下面是 C语言实现代码:

#include <stdio.h>

//调整堆 
void heapAdjust(int a[] ,int s,int m){
	int rc = a[s];
	for(int j = 2*s ;j<=m ;j*=2){
		if(j<m&&a[j]>a[j+1]){
			++j;	
		} 
		if(rc <= a[j]){
			break;	
		} 
		a[s] = a[j];
		s = j;
	}
	a[s] = rc;
}

void heapSort2(int a[] ,int n){
	//建立初始堆。 
	//从n/2 开始因为树的 最后一个非叶子结点就是2/n 
	for(int i = n/2 ;i>0 ;--i){
		heapAdjust(a,i,n);
	}
	int t;
	t = a[1];
	a[1] = a[n];
	a[n] = t;
	for(int i = n-1 ;i>1;--i){
		heapAdjust(a,1,i);
		t = a[1];
		a[1] = a[i];
		a[i] = t;
	}
}
void print(int a[] ,int n)
{
    for (int i =1;i<n;i++)
    {
        printf("%d  ",a[i]);
    }  
    printf("\n");
}

int main(int argc, char *argv[])
{
	int a[]=
    {
        0,59,48,75,98,86,23,37,60
    };
    print(a,9);
    heapSort2(a,8);
    print(a,9);
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值