堆与堆排序原理及代码实现

一、什么是堆

若数组a[n] 中的元素具有如下的性质:

(1)若2*i+1<n(保证2*i+1为数组中元素),则a[i]>=a[2*i+1];

(2)若2*i+2<n(保证2*i+1为数组中元素),则a[i]>=a[2*i+2];

则数组a[n]是一个堆,即任一节点的值不小于它的左子节点的值,也不小于它的右子节点的值。

注:元素a[i]的父节点为a[(i-1)/2],其中除号为整除,且元素a[i]的两个子节点分别为a[2*i+1],a[2*i+2]


二、堆排序

对一个数组进行堆排序,首先要把数组变成一个堆,即数组的堆化,然后进行堆排序。
(1)数组的堆化
  从第一个不是叶节点的节点开始调整。因为数组的最后一个元素为a[n-1],故其父节点为a[(n-1-1)/2],即a[(n-2)/2]。在如下图1中,n = 9,a[(n-2)/2] = a[3] = 68

                                                                                                             

                                                           图1                                                                     图2                                                                               图3
首先,i = 3,比较a[3] = 68的两个左右子节点,找出对大的;然后把子节点中最大的元素与a[3]比较,若a[3]小于较大的子节点,则交换,即完成以a[3]为顶点的子树的堆化,结果为图2;同理,i = 2,同理将22与42交换,可完成以a[2]为顶点的堆排序,结果为图3;当i = 1时,将26与84交换,然后再将26与68交换,使得以a[1] = 26为顶点的子树堆化,过程如下图:
                                                                                                        

                                                                               图4                                                                           图5           

同理,i = 0时,46与84交换,同时只需修改以a[0]为顶点的左分支,因为右分支原本就是堆,过程如下图:

                                                                                 
                                                   图6                                                       图7                                                               图8
代码如下:
/*ra为需要排序的数组,l 为需要堆化的起始序号,r为需要堆化的终止序号,前提为i以下所有节点都已经堆化*/
template<typename _Tp> void sift_down(vector<_Tp> &ra, const int l, const int r)
{
int i = l;//i 表示顶端的节点
int j;
_Tp a;
while ((2 * i + 1) <= r)//若存在子节点
{
j = 2 * i + 1;

//找出两个左右子节点中比较大的,定位在j处
if ((j < r) && (ra[j] < ra[j + 1]))
{
j++;
}

//看是否需要与子节点交换
if (ra[i] < ra[j])
{
a = ra[i];
ra[i] = ra[j];
ra[j] = a;
i = j;//下次从这个点开始调整
}
else
{
break;
}
}
}

(2)堆排序
  根据堆的性质可知,一个堆的顶点元素a[0]是堆中的最大元素。将a[0]与a[n-1]交换,此时a[n-1]是最大的元素,然后将数组a[0]a[1].........a[n-2]堆化;同理继续将a[0]与a[n-2]交换,此时a[n-2]为数组中第二大的元素,且堆化a[0]a[1].........a[n-3];依次类推,共需n-1调整和对换,在进行第i次对调前,a[0]a[1].........a[n-i]是一个堆,当a[0]与a[n-i]对调后,a[n-i]是第i个大的节点,这时将a[0]a[1].........a[n-i-1]堆化。最后可得到一个递增的数组。
代码如下:
/*heap_sort堆排序的主题流程*/
template<typename _Tp> void heap_sort(vector<_Tp> &ra)
{
int i, j;
int n = ra.size();

//堆化数组,从第一个非叶节点开始,依次调整为堆
for (i = (n - 2) / 2; i >= 0; i--)
{
sift_down2(ra, i, n - 1);
}

//对堆进行排序
for (j = 1; j < n; j++)
{
//把ra[0]与数组的最后一个元素交换
swap(ra[0],ra[n-j]);

//堆化数组
sift_down2(ra, 0, n - j - 1);
}

return;
}


第一次写博客,希望大家多多指教,共同学习!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值