堆排序——HeapSort

堆排序:

堆排序与归并排序的时间复杂度一样,同为O(nlogn),但与归并排序不同的是,堆排序具有空间原址性:任何时候都只需要常数个额外的空间存储临时数据。

堆的定义:

(二叉)堆是一个数组,可以被看成是一个近似的完全二叉树。树上的每个结点对应数组中的一个元素。除了最顶层外,树应该是充满的,且为从左向右填充。

二叉堆通常分为两种:最大堆和最小堆。
最大堆:堆中的最大元素存放在根节点中,并且在任何一个子树中,该子树所包含的所有结点的值都不大于该子树的根结点的值。最大堆性质如下:

            A[i / 2] >= A[i]

最小堆的性质与最大堆正好相反:

            A[i / 2] <= A[i]

要使用堆排序,就要熟悉堆的基本操作:维护堆的性质(最大堆为例)。

给性一个数组A和其一个下标i,在维护堆的性质时,i的左孩子或右孩子有可能大于i结点的值,这样就不符合堆的性质了。要维护最大堆的性质,就要让A[i]的值在最大堆中“逐级下降”,使得以下标为i为根结点的子树重新具有最大堆的性质。

下面是维护最大堆过程:

/* length为数组是长度,i为最后一个非叶子结点 */
void Max_Heapify(int A[], int length, int i)//维护最大堆
{
    int left = 2 * i;
    int right = 2 * i + 1;
    int largest;  //largest暂存值最大的节点的下标
    if(left < length && A[left] > A[i])
        largest = left;
    eles
        largest = i;
    if(right < length && A[right] > A[largest])//选出结点值最大的下标
        largest = right;
    if(i != largest)//若最大值不为根结点,则把最大值交换至根节点处
        swap(&A[largest], &A[i]);
        Max_Heapify(A, length, largest);//继续维护以largest为根结点的子树
}

有了维护最大堆的过程,下面就可以由给出的数组来建一个最大堆了。字数组A([n / 2]…n)中的元素都是叶子结点,每个叶子结点可以看成只包含一个元素的堆。

void BuildHeap(int A[], int length)
{
    int i;
    for(i = length / 2; i >= 1; i--)//从最后一个非叶子结点开始建最大堆
        Max_Heapify(A, length, i);
}

开始时,堆排序算法利用Max_Heapify将输入的数组A[]建成最大堆,由于数组中的最大元素总是存放在根结点A[1]中,通过让它与A[n]互换,可以将最大值放在正确的位置。接下来,将n结点去掉,n的孩子结点仍然满足最大堆的性质,而新的根结点不一定满足,所以就要调用Max_Heapify()来调整新的根节点。以使其满足最大堆性质,从而在A[1…n-1]上新建一个最大堆。重复这一过程,直至堆的大小从n - 1降到2.

代码如下:
void heap_sort(int A[], int length)
{
    int size = length;
    BuildHeap(A, size);     //建立最大堆
    while(size > 1)
    {
        swap(&A[size - 1], &A[1]);   //每次将最大的值放到正确的位置
        size--;
        Max_Heapify(A, size, 1);  //将剩下的元素重新建堆
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值