算法设计与分析——堆排序(Java)

【问题】应用堆排序 (heap sort) 方法对一个记录序列进行升序排列。 (heap) 是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子结点的值(小根堆);或者每个结点的值都大于或等于其左右孩子结点的值(大根堆)。如果将具有 n 个结点的堆按层序从 1 开始编号,则结点之间满足如下关系: 

 以结点的层序编号作为下标,将堆用顺序存储结构(即数组)来存储,则堆对应于一组序列,如下图所示。

 

【想法】堆排序是利用堆(假设利用大根堆)的特性进行排序的方法,其基本思想是:首先将待排序的记录序列构造成一个堆,此时,堆顶记录是堆中所有记录的最大者,将它从堆中移走(通常将堆顶记录和堆中最后一个记录交换),然后将剩余记录再调整成堆,这样又找出了次大记录,依此类推,直到堆中只有一个记录为止,如下图所示。 

如何将一个无序序列调整为堆是堆排序算法的关键,筛选法调整是成功应用减治法的例子。如下图所示,图 (a) 是一棵完全二叉树,且根结点 28 的左右子树均是堆。为了将整个二叉树调整为堆,首先将根结点 28 与其左右子树的根结点比较,根据堆的定义,应将 28 与 35 交换,如图 (b) 所示。经过这一次交换,破坏了原来左子树的堆结构,需要对左子树再进行调整,调整后的堆如图 (c) 所示。

由这个例子可以看出,在堆调整的过程中,总是将根结点(即被调整结点)与左右子树的根结点进行比较,若不满足堆的条件,则将根结点与左右子树根结点的较大者进行交换,这个调整过程一直进行到所有子树均为堆或将被调整的结点(即原来的根结点)交换到叶子为止。这个自堆顶至叶子的调整过程称为筛选(sieve)。

【算法】假设当前要筛选结点的编号为 k,堆中最后一个结点的编号为 n,且结点 k 的左右子树均是堆(即 r_{k+1}~r_{n} 满足堆的条件) 筛选算法用伪代码描述如下。

算法:筛选法调整堆 SiftHeap
输入:r_{k+1}~r_{n} 满足堆的条件,待筛选的记录 r_{k}

输出:{r'_{k}r'_{k+1},...,r'_{n}} 为大根堆

1 设置 i 和 j ,分别指向当前要筛选的结点和要筛选结点的左孩子;

2 若 r_{i} 已是叶子,则筛选完毕;
   否则,比较要筛选结点的左右孩子结点,并将j指向值较大的结点; 

3 将 r_{i} 和 r_{j} 进行比较,有以下两种情况:

    3.1 如果 r_{i} > r_{j}​,则完全二叉树已经是堆,筛选完毕;

    3.2 否则将 r_{i} 和 r_{j}  交换;令 i = j,转步骤 2 继续进行筛选。

【算法分析】算法 Sift 将根结点与左右子树的根结点进行比较,若不满足堆的条件,则将根结点与左右子树根结点的较大者进行交换,所以,每比较一次,需要调整的完全二叉树的问题规模就减少一半,因此,其时间性能是O(log_{2}n)
【算法实现】堆排序首先将无序序列调整成堆,由于叶子结点均可看成是堆,因此,可以从编号最大的分支结点直至根结点反复调用筛选算法。堆排序算法用JAVA语言描述如下:

public class SiftHeap {
    public static void main(String[] args)
    {
        int r[]={47,33,35,2,18,71,26,13};
        HeapSort(r,8);
        for(int i=0;i<8;i++)
            System.out.print(r[i]+" ");
    }
    static void SiftHeap(int r[], int k, int n)
    {
        int i, j, temp;
        i = k; j = 2 * i +1;                      //置i为要筛的结点,j为i的左孩子
        while (j < n)                       //筛选还没有进行到叶子
        {
            if (j < n-1  && r[j] < r[j+1]) j++;    //比较i的左右孩子,j为较大者
            if (r[i] > r[j])                     //根结点已经大于左右孩子中的较大者
                break;
            else {
                temp = r[i]; r[i] = r[j]; r[j] = temp;            //将被筛结点与结点j交换
                i = j; j = 2 * i+1;                 //被筛结点位于原来结点j的位置
            }
        }
    }
    static void HeapSort(int r[], int n)
    {
        int i, temp;
        for (i = (n-1)/2; i >= 0; i--)       //初始建堆,从最后一个分支结点至根结点
            SiftHeap(r, i, n) ;
        for (i = 1; i <= n-1; i++)         //重复执行移走堆顶及重建堆的操作
        {
            temp = r[0]; r[0] = r[n-i]; r[n-i] = temp;
            SiftHeap(r, 0, n-i);            //只需调整根结点
        }
    }
}

运行结果如下:

from:算法设计与分析(第2版)——王红梅 胡明 编著——清华大学出版社

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值