Java中的十大排序(五)堆排序

143 阅读2分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

前言

上一篇章我们使用了选择排序这个广为人知的排序算法,他是非常简单的。而本章我们将要给大家讲解另一个比较难懂的排序,堆排序,他主要利用了完全二叉树的特点来实现。

image.png

堆排序分析

堆排序的复杂度: 时间复杂度平均为O(nlog₂n) 时间复杂度最坏为O(nlog₂n) 时间复杂度最好为O(nlog₂n) 空间复杂度为O(1) 稳定性为不稳定

由上述时间复杂度来看堆排序的速度优势特别大,这也是得益于他的堆完全二叉树构造,并且空间复杂度也适中,他的所有复杂度综合来看目前属于我们前几期排序的第一名。

堆的排序思路

堆分为两种 大顶堆 和 小顶堆。 大顶堆的特点是每个节点的值都大于或等于其左右孩子节点的值,所以每次从底部开始向上进行对比,然后把大的节点往上移动。 小顶堆的特点是每个节点的值都小于或等于其左右孩子节点的值,然后也开始进行对比,把小的节点往上移动。 先构造一个大顶堆 7 | | 5 4 || || 32 12 上面可以看出虽然这个二叉树中不是有序的,但是他的每个根节点都比下面的节点大,所以这是一个大顶堆,虽然不是有序的但是我们只关注他的顶堆,因为我们后续的排序就是根据顶堆来进行排序。

小顶堆与上面大顶堆异曲同工。

堆的代码实现

堆排序的主要思路:通过大顶堆与小顶堆的构造来实现对无序数列的升序与排序,假设我们现在要降序,那么我们要构造得就是小顶堆,通过每次构造来获得最小的数,把最小数存进一个数组来进行排序。 代码实现:

@Test
public void headSort() {

  //构造一个无序堆,后面进行对堆的排序
  for (int i = (arr.length) / 2 - 1; i >= 0; i--) {
    headAdjust(arr, arr.length, i);
  }
  //构造最小堆,把最大的放在队尾把最小的放在顶部
  for (int i = arr.length - 1; i >= 1; i--) {
    int temp = arr[0];
    arr[0] = arr[i];
    arr[i] = temp;
    //交换堆位置
    headAdjust(arr, i, 0);
  }

  System.out.println(getArr(arr));
}

/**
* 这个方法主要用于堆中进行交换获取顶部最小堆的数
*/
private static void headAdjust(int[] list, int len, int i) {
  int k = i, temp = list[i], index = 2 * k + 1;
  while (index < len) {
    if (index + 1 < len) {
      if (list[index] < list[index + 1]) {
        index = index + 1;
      }
    }
    if (list[index] > temp) {
      list[k] = list[index];
      k = index;
      index = 2 * k + 1;
    } else {
      break;
    }
  }
  list[k] = temp;
}