堆排序及java版实现

Posted 不识君的荒漠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆排序及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;
    

以上是关于堆排序及java版实现的主要内容,如果未能解决你的问题,请参考以下文章

Java版高级数据结构堆树&堆排序

堆排序及手写堆的基本操作java代码

选择排序——2堆排序实现

堆排序的原理及python实现

排序--08---堆排序

java 各类排序数组实现