数据结构--堆排序

简介:     通过学习优先队列和二叉堆,我们知道优先队列中队首的元素总是最大(最大堆)或者最小(最小堆)的元素,根据这个规律,如果我们把一系列无序的元素插入到优先队列中,然后再从优先队列中逐个删除元素,则删除元素的顺序是有序的。

    通过学习优先队列和二叉堆,我们知道优先队列中队首的元素总是最大(最大堆)或者最小(最小堆)的元素,根据这个规律,如果我们把一系列无序的元素插入到优先队列中,然后再从优先队列中逐个删除元素,则删除元素的顺序是有序的。我们由此可以演变得出一种排序算法--堆排序。
    此处以最大堆来讨论,堆排序的实现分为2个阶段:构造堆阶段下沉排序阶段

  • 构造堆阶段:通过下沉操作,将新添加到堆中的元素放到堆的根节点,然后与较大的子节点比较,如果小于子节点,则与子节点交换位置,使得较小的元素在堆中不断下沉,放入堆中合适的位置。
  • 下沉排序阶段:构造堆完成后,堆的根节点存放的是最大的元素。排序阶段,每次将根节点元素与堆数组末尾元素交换位置,然后缩小堆的size,使得最大的元素从堆中剔除,放在数组末尾。然后通过下沉操作,使得根节点的新元素重新下沉到合适的位置。如此往复,直到堆中没有元素。此时,堆数组中的元素变为升序排列。

首先实现堆排序中需要的3个基本操作:

  • 比较(less):比较两个元素的大小,元素需要实现Comparable接口,从而可以被比较;
  • 交换(exch):交换两个元素在数组中的位置;
  • 下沉(sink):通过比较和交换,自上而下的移动元素,使其达到合适的位置;

    排序方法sort实际是对上面3个基本操作的组合,在构造堆阶段,通过sink操作构造好二叉堆。在排序阶段,通过exch操作将最大的元素排除到堆外,放到数组最末端,然后通过sink操作使堆重新变得有序。如此往复,直至堆中没有元素,此时数组即是有序的。基于最大堆的堆排序是升序的,反之,基于最小堆的堆排序是降序的。完整代码如下:

/**
 * 堆排序实现:基于最大堆实现
 */
public class HeatSort {

    /**
     * 比较
     */
    private static boolean less(Comparable[] arr, int i, int j) {
        return arr[i - 1].compareTo(arr[j - 1]) < 0;
    }

    /**
     * 交换
     */
    private static void exch(Comparable[] arr, int i, int j) {
        Comparable tmp = arr[i - 1];
        arr[i - 1] = arr[j - 1];
        arr[j - 1] = tmp;
    }

    /**
     * 下沉
     */
    private static void sink(Comparable[] arr, int k, int size) {

        while (2 * k <= size) {

            // 获取较大子节点的索引
            int j = 2 * k;
            if (j < size && less(arr, j, j + 1)) {
                j++;
            }

            // 如果当前节点大于子节点,则找到合适位置
            if (!less(arr, k, j)) {
                break;
            }

            // 如果当前节点小于子节点,则继续交换下沉
            exch(arr, k, j);
            k = j;
        }
    }

    /**
     * 排序
     */
    public static void sort(Comparable[] arr) {

        int size = arr.length;

        // 构造堆
        for (int k = size / 2; k >= 1; k--) {
            sink(arr, k, size);
        }

        // 下沉排序
        while (size > 1) {
            exch(arr, 1, size--);
            sink(arr, 1, size);
        }
    }

    public static void main(String[] args) {

        Integer[] arr = {3, 6, 5, 4, 7, 9, 8, 0, 2, 1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

从上面代码可以看出,堆排序使用待排数组本身作为堆的存储结构,不占用额外的空间,因而属于内部排序。

效率对比

image

    堆排序的主要工作都是在下沉排序阶段完成的,每次从堆中删除最大的元素,然后放入到堆缩小后空出的空间中。这个过程和选择排序的过程类似,都是选择最大/最小的元素放到数组末尾,但相比于选择排序,堆排序在查找最大/最小元素的效率为$O(logN)$,高于选择排序的$O(N)$,从而提升了排序效率。

参考

算法(第4版)2.4节-优先队列
coursera算法视频课程
堆排序示意动画

目录
相关文章
|
5月前
|
算法 Python
数据结构算法--4堆排序
堆排序过程概述:建立大根堆,将堆顶最大元素移出并替换为末尾元素,调整保持堆性质,重复此过程直至堆为空,实现排序。时间复杂度为O(nlogn)。Python中可用heapq模块进行堆操作。
|
6月前
|
机器学习/深度学习 人工智能 算法
数据结构与算法:堆排序和TOP-K问题
朋友们大家好,本节内容来到堆的应用:堆排序和topk问题
数据结构与算法:堆排序和TOP-K问题
|
6月前
|
算法
数据结构——堆的应用 堆排序详解
数据结构——堆的应用 堆排序详解
数据结构——堆的应用 堆排序详解
|
27天前
|
算法 搜索推荐
数据结构与算法学习十八:堆排序
这篇文章介绍了堆排序是一种通过构建堆数据结构来实现的高效排序算法,具有平均和最坏时间复杂度为O(nlogn)的特点。
65 0
数据结构与算法学习十八:堆排序
|
4月前
|
存储 算法
【数据结构】堆,堆的实现,堆排序,TOP-K问题
数据结构——堆,堆的实现,堆排序,TOP-K问题
47 1
【数据结构】堆,堆的实现,堆排序,TOP-K问题
|
3月前
|
搜索推荐 算法
【初阶数据结构篇】插入、希尔、选择、堆排序介绍(上篇)
堆排序(Heapsort)是指利⽤堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀ 种。它是通过堆来进⾏选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。
17 0
|
3月前
|
算法
【初阶数据结构篇】堆的应用(堆排序与Top-K问题)
即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
40 0
|
4月前
|
搜索推荐
【数据结构常见七大排序(二)】—选择排序篇【直接选择排序】And【堆排序】
【数据结构常见七大排序(二)】—选择排序篇【直接选择排序】And【堆排序】
|
6月前
|
算法
[数据结构]——二叉树——堆排序
[数据结构]——二叉树——堆排序
|
6月前
|
存储 搜索推荐 算法
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)