堆排序(heap sort)

堆和堆排序

堆排序结合了合并排序和插入排序的优点,其时间复杂度为O(n*logn),且是一种原地排序算法(在任何时候,数组中只有常数个元素存储在输入数组以外)

堆排序采用了名为“堆”的数据结构,分为大顶堆和小顶堆。堆排序采用了大顶堆,其定义是:一颗完全二叉树,且除根结点外的任意结点的值至多和其父结点的值一样大。小顶堆组织方式刚好相反,其通常用在构造优先队列时使用

为方面起见,实际代码中用数组表示堆

堆的关键操作是“维持大顶堆性质”(记为MAX_HEAPIFY),即堆中结点变动后,对堆进行适当调整,使其重新满足最大堆的性质。MAX_HEAPIFY是一种递归操作,其步骤如下:

  • 比较结点a[k]与其左右子树根结点a[2k+1], a[2k+2]的大小,若a[k]不是最大的,则将a[k]与最大值结点交换位置
  • 重复上面的步骤,直至a[k]比其左右子树根结点都大或为叶节点为止
然后是“建堆”操作,具体方法为: 从完全二叉树的最后一个非叶结点a[n/2-1]开始,逆遍历至根结点结束,逐个进行MAX_HEAPIFY操作

了解以上两个操作后,堆排序步骤如下:
  • 建立最大堆
  • 将根结点与最后一个叶结点交换,堆大小减1
  • 对新的根结点使用MAX_HEAPIFY操作
  • 重复步骤2,3直到堆为空

代码
void swap(int * a, int * b){
	assert(a != NULL);
	assert(b != NULL);

	if(a != b){
		*a ^= *b;
		*b ^= *a;
		*a ^= *b;
	}
}

void max_heapfy(int a[], int n, int k){  //k是扰乱大顶堆性质的结点的下标
	assert(a != NULL);
	assert(n > 0);
	assert(k >= 0 && k < n); 

	int max = k;
	if(k*2 < n && a[k*2] > a[max]) max = k*2;
	if(k*2+1 < n && a[k*2+1] > a[max]) max = k*2+1;  //判断哪个是最大结点
	if(max != k){  //递归进行大顶堆性质维持
		swap(&a[k], &a[max]);
		max_heapfy(a, n, max);
	}
}


void heap_sort(int a[], int n){
	assert(a != NULL);
	assert(n > 0);

	for(int i = n/2-1; i >= 0; i--)  //建堆,最后一个非叶结点为a[n/2-1]
		max_heapfy(a, n, i);
	for(int i = n-1; i >= 1; i--){  //排序时,每次交换堆顶元素和堆尾元素,然后进行大顶堆性质维持,堆的大小是不断减小的
		swap(&a[0], &a[i]);
		max_heapfy(a, i, 0);  //堆的大小不断减小
	}
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值