堆排序及java版实现

堆排序

特点

  • 原地排序
  • 时间复杂度O(nlogn)

步骤

  1. 构造堆
  2. 排序

堆排序的关键就是借助“堆”这个数据结构对元素进行排序,下面简单介绍一下堆。

定义

  • 堆是一棵完全二叉树
  • 堆中每一个节点的值都必须大于等于其子树每个节点的值(大顶堆),或者是小于等于其子树每个节点的值(小顶堆)

大、小顶堆可以这样来记,顶部节点是最大的,就是大顶堆;顶部节点是最小的,就是小顶堆。

操作

在堆中插入、删除一个节点都要进行调整以满足堆的定义,这个调整的过程我们一般叫做堆化(heapify)。

堆化有两种:从下往上和从上往下(自底向上和自顶向下)。堆化的本质就是为了满足堆定义的特性。

插入

堆是一棵二叉树,插入元素就是在尾部插入一个叶子节点,插入之后跟它的父节点比较,是否满足定义的大小关系,如不满足就进行交换,重复这个过程。

这是从下往上的过程,以小顶堆为例就是拿某个节点和它的父节点比较,如果小于父节点就交换。

删除

每次删除的是根结点,过程是:将最后一个叶子节点和根节点交换,删除最后一个叶子节点。此时,将根节点和它的子节点比较,如果不满足定义的大小关系就交换,一直重复这个比较、交换的过程。

这个就是从上往下。

存储结构

虽然这是一个二叉树,本文采用数组作为堆的底层存储。

根节点在数组的索引为1的地方,索引0不存数据。它的左子节点索引为2*1,右子节点索引为2*1+1。

所以,对任何一个节点的索引i,它的左子节点为2*i,右子节点为2*i+1。

如果从索引0开始,则它的左子节点是2*i+1,右子节点是2*i+2,需要多加一次加法运算。

所以从索引1开始,算是一种优化,但是在实际排序的时候,待排序的数组默认是从0开始的。

排序实现

排序步骤前面说了:1、建堆,2、排序

快速实现

在java中提供的有现成的数据结构已经实现了堆这种数据结构,因此我们可以取个巧,借助这种数据结构,而不需要自己手动建堆,直接进行排序即可。

这个就是java中的优先级队列:PriorityQueue。

代码示例如下:

public class QuickHeapSort {

    public void sort(int[] nums) {
        // 默认是自然排序,也就是小顶堆,方便从小往大排序
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        // 构造堆
        for (int n : nums) {
            queue.offer(n);
        }
        // 排序
        for (int i = 0; i < nums.length; i++) {
            // poll后, 内部会进行堆化,满足堆的定义
            nums[i] = queue.poll();
        }
    }
}

标准实现

这里标准实现指的是不借助java提供的数据结构,自已建堆、排序,写法也比较多样,以下示例仅供参考。

public class HeapSort {

    public void sort(int[] nums) {

        // 建堆
        for (int i = nums.length / 2 - 1; i >= 0; i--) {
            heapify(nums, i, nums.length);
        }

        // 排序,依次从当前节点堆化进行调整
        for (int i = nums.length - 1; i >= 0; i--) {
            // 将当前堆的最大节点放到i位置
            swap(nums, 0, i);
            // 堆化[0,i)之前元素
            heapify(nums, 0, i);
        }
    }

    private void heapify(int[] nums, int i, int length) {
        // 大顶堆的节点调整
        while (true) {
            int maxPos = i;
            // 检查当前节点的值是不是小于它的左子节点
            if (i * 2 + 1 < length && nums[i] < nums[i * 2 + 1]) {
                maxPos = i * 2 + 1;
            }
            // 和i节点的右子节点也比较下,为了找出最大值的节点
            if (i * 2 + 2 < length && nums[maxPos] < nums[i * 2 + 2]) {
                maxPos = i * 2 + 2;
            }
            if (maxPos == i) {
                // 说明已经找不到比当前节点大的了
                break;
            }
            // 交换两个节点
            swap(nums, i, maxPos);
            // 继续往下处理这个过程
            i = maxPos;
        }
    }

    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不识君的荒漠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值