【Java】基础排序算法-堆排序

本文介绍了Java中的堆排序算法,包括堆的概念、存储方式、如何构建最大堆以及堆排序的时间复杂度和空间复杂度分析。堆排序是一种选择排序,通过堆结构快速找到无序区间的最大值并进行交换,其特点是不稳定且效率较高。
摘要由CSDN通过智能技术生成

【Java】基础排序算法-堆排序


前言

堆排序的基本原理也是选择排序,只是不再使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
在这里插入图片描述


一、堆(heap)

概念
  1. 堆逻辑上是一棵完全二叉树(下文称其为:二叉堆),因此二叉堆物理上是保存在数组中,且不会有空间的浪费;
  2. 若堆满足任意结点的值都大于其子树中结点的值,则此堆叫做大堆,或者大根堆;
  3. 若堆满足任意结点的值都小于其子树中结点的值,则此堆叫做小堆,或者小根堆;
  4. 堆的基本作用是可以快速找集合中的最值,可以辅助实现集合的排序问题;
堆在数组中的存储
1. 已知双亲(parent)的下标,则:
	a. 左孩子(left)下标 = 2 * parent + 1;
	b. 右孩子(right)下标 = 2 * parent + 2;
2. 已知孩子(不区分左右)(child)下标,则:
	c. 双亲(parent)下标 = (child - 1) / 2;

二、将数组调节为最大堆或最小堆

最大堆为例:(向下调整)
1.找到数组中代表二叉堆里最后一个非叶子节点的位置索引index(即找到最后一个叶子节点的双亲节点:index = (数组最后一个元素索引-1) / 2);
2.以步骤1找到节点为根节点与其左右子树值进行比较(向下调整);
	根据最大堆特点,根节点值应该为当前树中的最大值,所以:
		a.若根节点值大于左右孩子的值,则说明此小树已经满足最大堆条件;
		b.若根节点值小于左右孩子中的任意一个,则将根节点值与左右孩子中的较大值进行交换;
		c.以步骤b交换值后的那个孩子为根节点,重复a、b、c步骤,不断向下调整,直到满足步骤a的条件;
3.index--,再重复2步骤,直到将index = 0 即根节点处理结束后,此时的二叉堆就是一个最大堆;

三、 利用最大堆进行排序:

根据最大堆特点,堆顶元素为当前堆中最大值
	将数组进行区间划分:[0,arr.length-i)为待排序区间;[arr.length-i,arr.length)为已排序的升序区间,i初始值为0;
		a.将数组[0,arr.length-i)区间调整为最大堆,此时待排序区间首元素为最大值;
		b.将待排序区间首元素值与尾元素值进行交换,此时最大值到区间最后即i的位置;
		c.待排序区间缩小即i++;已排序区间随之扩大
		d.将交换到区间首位置的元素进行向下调整,使待排区间满足最大堆条件
	重复a、b、c、d步骤,直到整个数组变得升序有序
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定,有大量元素交换过程
代码实现:
    /**
     * 堆排序
     * 排升序用大堆;排降序用小堆
     * 以排升序为例
     */
    public static void heapSort(int[] arr) {
        //index为最后一个非叶子节点元素索引,即最后一个叶子节点的双亲节点
        int index = (arr.length - 1 - 1) / 2;
        //将数组调节为最大堆
        for (; index >= 0; index--) {
            siftDown(arr, index, arr.length);
        }
        //堆顶元素依次交换至末尾
        for (int i = arr.length - 1; i > 0; i--) {
            int max = arr[0];
            arr[0] = arr[i];
            arr[i] = max;
            //调整待排区间为最大堆
            siftDown(arr, 0, i);
        }
    }

    //向下调整,实现最大堆过程
    public static void siftDown(int[] arr, int index, int length) {
        //index位置对应的左孩子索引为2 * index + 1
        //越向下调整,index的子节点索引值越大
        while (2 * index + 1 < length) {
            //当前index位置的左孩子
            int k = 2 * index + 1;
            //找左右孩子中的较大值
            if (k + 1 < length && arr[k + 1] > arr[k]) {
                k = k + 1;
            }
            //此时满足最大堆条件
            if (arr[index] > arr[k]) {
                break;
            } else {
                //将根节点值与左右孩子中的较大值进行交换
                int temp = arr[index];
                arr[index] = arr[k];
                arr[k] = temp;
                //以交换值后的那个孩子为根节点继续向下调整
                index = k;
            }
        }
    }
测试:

简单的对堆排序、希尔排序、插入排序进行测试,发现堆排序处理不同的数据集时效率还是非常不错的
在这里插入图片描述


内容如有任何不妥之处,恳请指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值