常见的排序算法:堆排序

2021/8/2 20:36:26

本文主要是介绍常见的排序算法:堆排序,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 1. 原理
  • 2. 代码实现
  • 3. 复杂度

1. 原理

  • 要了解堆排序我们需要先了解堆,堆分为大根堆和小根堆,大根堆就是一颗顺序存储的"二叉树",这棵二叉树的父亲节点的值大于任意孩子节点的值,这样的堆叫做大根堆。
  • 那么根据大根堆的性质,我们可以知道,大根堆中第一个元素是这个堆中最大的元素,那么我们每次将无需区间的最后一个元素与第一个元素交换,然后将最后一个元素添加到有序区间,将无需区间重新调整为一个大根堆,通过这样的调整,有序区间中元素个数增加的过程中,数组也趋于有序,这样的排序算法就是堆排序。
  • 那么我们可以使一个待排序数组变成一个"大根堆",然后通过上述的方法来进行排序。我们先调整每一个子树,使它成为一个大根堆,最后调整整棵树,这样这个数组就变成了一个大根堆。
  • 那么这个调整的过程是怎么样的呢?首先我们从堆可以知道,父亲节点的下标 parent 和左孩子节点的下标关系为 child = 2 * parent + 1,我们可以通过判断父亲节点的大小和左右孩子大小的最大值来判断这个树是不是大根堆,如果左右孩子大小的最大值大于父亲节点的值我们就交换这两个值。可是我们交换了之后以 child 下标节点为父亲节点的树也可能不再是大根堆了呀,那我们让 parent = child, child = 2 * parent + 1,继续调整它的子树不就行了吗。

请添加图片描述

2. 代码实现

  • 话不多说,上代码:
public void heapSort(int[] arr) {
    // 将数组调整为一个"大根堆"
    // (arr.length - 2) / 2 为堆的最后一个叶子节点的父节点
    for (int i = (arr.length - 2) / 2; i >= 0; i--) {
        adjustDown(arr, i, arr.length);
    }
    // 开始调整
    // 每次调整由于无需区间交换后的最后一个以及有序了,所以最后一个元素不需要调整
    int end = arr.length - 1;
    while (end > 0) {
        int tmp = arr[end];
        arr[end] = arr[0];
        arr[0] = tmp;
        adjustDown(arr, 0, end);
        end--;
    }
}

/**
* @param arr 待调整数组
* @param end 标记调整区间的长度
*/
public void adjustDown(int[] arr, int parent, int end) {
    int child = 2 * parent + 1;
    while (child < end) {
        // 保证 child 下标指向的元素是两个子节点中最大的
        if (child + 1 < end && arr[child] < arr[child + 1]) {
            child++;
        }
        if (arr[parent] < arr[child]) {
            int tmp = arr[parent];
            arr[parent] = arr[child];
            arr[child] = tmp;
            parent = child;
            child = 2 * parent + 1;
        } else {
            // 由于是从最后一个叶子节点开始调整,那么 arr[parent] > arr[child] 时
            // 以 arr[parent] 为根节点的这棵树以及是"大根堆"了,不需要继续向下调整了
            break;
        }
    }
}
  • 排序的稳定性:存在跳跃式交换,堆排序不稳定。

3. 复杂度

时间复杂度空间复杂度
O(n*logn)O(1)
数据不敏感数据不敏感
  • 小菜鸡一枚,大家有问题可以评论,大家一起讨论。


这篇关于常见的排序算法:堆排序的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程