堆排序之 大顶堆和小顶堆 c语言

百度得到的堆定义如下:

堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)

        当ki <= k2i的时候,称之为小顶堆,反之则称之为大顶堆。堆排序时间复杂度好坏情况均为nlogn,效率在一众排序算法中排得上第二了。此外,在很多面试题中,堆排序是一种非常高效的解决问题手段,比如查找前100万个数中的最大值或者最小值。此时如果使用堆排序的话,不仅满足时间复杂度要求,空间复杂度也可满足。堆排序听起来挺难的,其实实现是相当简单。

        堆的图形化显示和二叉树类似,对于小顶堆,任意节点的左右子树都小于它,堆顶为最小元素。对于大顶堆,任意节点都大于其左右子树,堆顶为最大元素,定义上容易理解。堆排序是基于数组的,因为数组下标和它特别匹配。如果使用c语言实现堆排序,注意分配数组的时候多分配一个存储单元,这样就可以直接从下标1开始,免得从0开始还得处理下标问题,得不偿失。

        堆的基本操作包括:查询,添加,删除,无序数组初始化建堆等。

1.       先从数组初始化建堆开始,首先要找到堆中最后一个非终端节点,下标为size/2。至于这个值是如何得到的。可以自行计算下,这里给个提示,没有孩子节点或者有一个孩子节点两种情况。得到这个非终端节点下标后,可以从它开始,自下往上调整堆,根据堆的性质(大顶堆或者小顶堆),将当前元素和孩子节点比较,然后调整合适位置。代码比较简单,如下:

//调整s的位置,s+1 ~ end为排好序的堆
void heapAdjust(pheap p, int s, int end)
{
	int i, j;
	j = 2 * s;

	for (;j <= end; j*=2)
	{
        //这里给出的是大顶堆,如果是小顶堆的话,将比较符号以及下面的操作进行反向处理即可。
		if (j < end && p->data[j] < p->data[j+1])j++;
		if (p->data[s] >= p->data[j]) break;
		swap(p->data + s, p->data + j);
		s = j;
	}
	return ;
}

void heapSort(pheap p)
{
	int i, size = p->len;

    //从非终端节点,自底向上调整堆
	for (i = size/2; i >= 1; i--)
		heapAdjust (p, i, p->len);
}

2.  查询操作,当然就是从顶部开始往下查喽,这点和普通的二叉搜索树查询一样,比较大小,然后再和左右子树比较。

3. 插入操作,通常都是追加到尾部,然后自底向上比较找到合适位置。

4. 删除操作的话,一般都是删除堆顶元素,这时候将堆底元素提到堆顶,然后重新调整堆即可。调整操作于初始化无序数组类似。

 

以上就是堆排序的内容,相比插入排序、归并排序、快速排序,我觉得堆排序算是相当高效的一种排序算法。此外,相比于利用AVL、红黑树等高级数据结构实现的排序而言,堆排序实现代码特别简洁,和冒泡排序有的一拼,适合手撕排序。

参考资料:

1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著

=============================================================================================

Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值