堆排序

基本思想
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
完全二叉树
在这里插入图片描述
上图就是一个完全二叉树,其特点在于:
1.从作为第一层的根开始,除了最后一层之外,第N层的元素个数都必须是2的N次方;第一层2个元素,第二层4个,第三层8个,以此类推。
2.而最后一行的元素,都要紧贴在左边,换句话说,每一行的元素都从最左边开始安放,两个元素之间不能有空闲,具备了这两个特点的树,就是一棵完全二叉树。
小顶堆:我们假设有一棵完全二叉树,在满足作为完全二叉树的基础上,对于任意一个拥有父节点的子节点,其数值均不小于父节点的值;这样层层递归,就是根节点的值最小。
大顶堆:同理,又有一棵完全二叉树,对于任意一个子节点来说,均不大于其父节点的值,如此递推,就是根节点的值是最大的
算法分析
1.假设结果要递增有序。
2.首先用前n个元素的无序序列,构建成大顶堆;
3.构建大顶堆时,从最后一个非叶节点n/2-1的位置开始检查节点与其孩子值是否满足大顶堆的要求,不满足则需要调整该元素与其孩子节点元素的位置,如果有调整,则调整过的孩子节点(子树)也要递归调用调整子树中的元素值位置,保证子树也是大顶堆。然后按照层次遍历的顺序依次往前调整所有非叶节点的值,最后根节点的值就是最大值。
4.得到大顶堆后将根节点与数组待排序部分的最后一个元素交换位置,即将最大元素"沉"到数组末端;
5.交换过后待排序数组长度减一,再对新长度的待排序数组重复上述过程,直到整个数组排序完成。如果我们要数组整体递增有序,则每次构建的是大顶堆;如果我们要数组整体递减有序,则每次构建的是小顶堆。
举例
现在对于堆排序来说,我们先要做的是,把待排序的一堆无序的数,整理成一个大根堆,或者小根堆,下面讨论以大根堆为例子。
给定一个列表array=[4,6,8,5,9],对其进行堆排序(使用大根堆)
构造初始堆:将给定的无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)
在这里插入图片描述
此时从最后一个非叶子节点开始(叶子节点不需要调整,第一个非叶子节点arr.length/2-1=5/2-1=1,也就是下面的6节点),从左至右,从上至下进行调整。
此时,把6和9比较交换后堆变为
在这里插入图片描述
找到第二个非叶子节点4,由于[4,9,8]中9最大,4和9交换,交换后的结果为
在这里插入图片描述
这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6后结果为
在这里插入图片描述
此时,我们将一个无序列构造成了一个大顶堆
调整堆:接下来将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
将堆顶元素9和末尾元素4进行交换后的结果为
在这里插入图片描述
重新调整结构,使其继续满足堆的定义
在这里插入图片描述
再将堆顶元素8与末尾元素5进行交换,得到第二大元素8
在这里插入图片描述
后续过程继续进行调整,交换,如此反复进行,最终得到整个序列有序
在这里插入图片描述
源代码

package KthElement;

import java.util.Arrays;

public class HeapSort {
    public static int[] heapSort(int[] arr) {
        if (arr == null || arr.length <= 1) return new int[]{};
        buildHeap(arr);//建堆
        int len = arr.length;
        while (len > 1) {
            //把堆顶元素和最后一个元素交换
            swap(arr, 0, len - 1);
            //交换完成后,去掉最后一个元素
            len--;
            //重新调整堆的顺序
            heapfy(arr, 0, len);
        }
        return arr;
    }

    private static void buildHeap(int[] arr) {
        //因为是满二叉树,所以最后一个非叶子节点在树的左边,而不是右边
        //最后一个非叶子节点:2i+1>=arr.length --> i>=(arr.length-1)/2
        for (int i = (arr.length - 1) / 2 - 1; i >= 0; i--) {
            heapfy(arr, i, arr.length);
        }
    }

    private static void heapfy(int[] arr, int i, int len) {
        while (true) {
            int max = i;
            int left = 2 * i + 1;//左孩子索引
            int right = 2 * i + 2;//右孩子索引
            //若左孩子大于最大值.则更新最大值
            if (left < len && arr[left] > arr[max])
                max = left;
            if (right < len && arr[right] > arr[max])
                max = right;

            if (max == i) {
                break;//若已经是大顶堆,则跳出循环
            } else {
                swap(arr, i, max);//若不是大顶堆,则交换位置
                i = max;
            }
        }
    }

    private static void swap(int[] arr, int i, int max) {
        int temp = arr[i];
        arr[i] = arr[max];
        arr[max] = temp;
    }

    public static void main(String[] args) {
        int[] ints = {6, 9, 1, 4, 5, 8, 7, 0, 2, 3};
        System.out.println("排序前:" + Arrays.toString(ints));
        System.out.println("排序后:" + Arrays.toString(heapSort(ints)));
    }
}

时间复杂度:T(n) = O(n log n)
稳定行:不稳定
使用场景:n大时较好

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值