数据结构排序算法——堆排序

堆排序属于选择排序,是对简单选择排序的一种优化。堆排序算法利用了完全二叉树的特点,这里的堆指的就是完全二叉树。完全二叉树的定义:除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。左对齐的意思就是一个非叶子结点只要有右孩子就一定有左孩子。堆排序中堆分为两种,一种大顶堆(父结点大于等于孩子结点,根结点最大),一种小顶堆(父结点小于等于孩子结点,根结点最小),本文以大顶堆为例。说明:网上关于堆排序的文章有好多,大部分讲的也非常好,但由于这一块不是很好理解,所以我在参考网上文章的同时,加入了自己的理解,疏漏之处欢迎读者批评指出。言归正传:

完全二叉树的特性:左孩子的位置=当前父节位置的2倍+1,右孩子的位置=当前父节点位置的2倍+2

这里位置指的是数组的下标(从0开始的那个)。

如根结点的下标为0,则根结点的左孩子的位置=0*2+1=1,根结点的右孩子的位置=0*2+2=2。

基本的知识介绍完以后,我们开始上代码:

public class HeapSort {
    /**
     * 堆排序函数
     * @param a
     */
    public static  void sort(int[] a){
        int n = a.length-1;
        for (int i = (n-1)/2;i>=0;i--){
            //i为最后一个根节点,n为数组最后一个元素的下标
            //i的值依次为3,2,1,0(假设数组的长度为9时)
            heapAdjust(a,i,n);
        }
        System.out.println("构造完成的大顶堆:");
        for (int b:a) {
            System.out.print(" "+b);
        }
        System.out.println();
        for (int i = n;i>0;i--){
            //把最大的数,也就是顶放到最后
            //i每次减一,因为要放的位置每次都不是固定的,i就是堆的相对最后位置
            swap(a,i);
            //再构造大顶堆
            heapAdjust(a,0,i-1);
        }
    }

    /**
     * 构造大顶堆函数,parent为父节点,length为数组最后一个元素的下标
     * @param a
     * @param parent
     * @param length
     */
    public static void heapAdjust(int[] a,int parent,int length){
        //定义临时变量存储父节点中的数据,防止被覆盖
        int temp = a[parent];
        //parent*2+1是其左孩子的节点
        for (int i = parent*2+1;i<= length;i=i*2+1){
            //如果左孩子小于右孩子,就让i指向右孩子
            if (i<length && a[i]  < a[i+1]){
                i++;
            }
            //如果父节点大于较大的孩子,那就退出循环
            if(temp>a[i]){
                break;
            }
            //如果父节点小于或等于较大的孩子节点,那就把孩子节点放到父节点上,这里体现了堆排序的不稳定
            a[parent] = a[i];
            //把孩子结点的下标赋值给parent,让其继续循环以保证大根堆构造正确
            parent = i;
        }
        //将刚刚的父节点中的数据赋值给新位置
        a[parent] = temp;
    }

    /**
     * 定义swap函数
     * 功能:将根元素与最后位置的元素交换
     * 注意这里的最后是相对最后,是在变化的
     * @param a
     * @param i
     */
    public  static void swap(int[] a,int i){
        int temp = a[0];
        a[0] = a[i];
        a[i]= temp;
    }
}

关键地方在代码中都有标注,这里再说一下比较重要的一个地方。我觉得理解左孩子的位置=父节点位置*2+1是相当重要的,这对我们理解构造大顶堆很关键。

最后再贴一下测试代码:

@Test
    public void testHeap(){
        //定义整型数组
        int[] a = {1,5,6,8,7,2,3,4,9};
        HeapSort.sort(a);
        //输出排序后的数组
        System.out.println("输出排序后的数组:");
        for (int n:a) {
            System.out.print(" "+n);
        }
    }

个人心得:当不能理解堆排序的过程时,就在理解原理的基础上多写几遍代码,或者在纸上画一下堆排序的过程,对我们理解堆排序算法有很大的帮助。

参考文献:https://www.cnblogs.com/Java3y/p/8639937.html

https://blog.csdn.net/qq_39776901/article/details/77387923

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值