一文了解面试中最常考的排序算法——堆排序
作者头像
  • 刘思
  • 2020-05-10 19:01:29 4

前言

如图所示,排序算法主要分为两大类:比较型排序和非比较型排序。根据功能的不同,排序算法又可以细分为交换类、选择类、归并类、插入类和计数类。之前的文章已经介绍了交换排序中的快速排序、归并排序中的二路归并排序以及非比较排序中的基数排序。接下来,我们将重点讨论选择排序中的堆排序。

二叉树基础

堆可以视为一种特殊的完全二叉树,其节点满足一定的条件:子节点的键值或索引总是小于(或大于)父节点。因此,堆可分为最大堆和最小堆。

完全二叉树简介

完全二叉树是一种特殊的二叉树,其中除了最后一层可能不满之外,其余各层都是满的。满二叉树是每一层都填满节点的二叉树,一个深度为m的满二叉树有(2^m - 1)个节点,且第m层有(2^{m-1})个节点。如下图所示:

完全二叉树

堆排序

下面以最大堆为例介绍堆排序的具体过程。最小堆的处理方式类似。堆排序的核心在于节点的调整,以确保每个节点都大于其子节点(对于最大堆而言)。

堆排序的步骤

  1. 构建初始堆:将初始无序序列构建成一个大根堆,即每个父节点的值都大于其左右子节点的值。
  2. 调整堆结构:将堆顶元素(最大值)与最后一个元素交换位置,然后重新调整堆结构,确保剩余部分仍然满足堆的性质。
  3. 重复操作:不断重复上述步骤,直到所有元素都被正确排列,最终形成有序序列。

示例

假设初始待排序序列为 [1, 8, 9, 3, 4, 6, 7]。

  1. 构建初始堆: 构建初始堆

  2. 调整堆结构:将根节点与最后一个节点交换位置,得到无序序列 [1, 8, 7, 3, 4, 6] 和有序序列 [9]。 第一次调整

  3. 继续调整:重复上述步骤,最终得到有序序列 [1, 3, 4, 6, 7, 8, 9]。 最终结果

堆排序的代码实现

Python 实现

```python def heapify(arr, n, i): largest = i left = 2 * i + 1 right = 2 * i + 2

if left < n and arr[i] < arr[left]:
    largest = left

if right < n and arr[largest] < arr[right]:
    largest = right

if largest != i:
    arr[i], arr[largest] = arr[largest], arr[i]
    heapify(arr, n, largest)

def heap_sort(arr): n = len(arr)

for i in range(n // 2 - 1, -1, -1):
    heapify(arr, n, i)

for i in range(n - 1, 0, -1):
    arr[i], arr[0] = arr[0], arr[i]
    heapify(arr, i, 0)

```

Java 实现

```java public class HeapSort { public static void heapify(int[] arr, int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2;

    if (left < n && arr[i] < arr[left]) {
        largest = left;
    }

    if (right < n && arr[largest] < arr[right]) {
        largest = right;
    }

    if (largest != i) {
        int swap = arr[i];
        arr[i] = arr[largest];
        arr[largest] = swap;
        heapify(arr, n, largest);
    }
}

public static void heapSort(int[] arr) {
    int n = arr.length;

    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }

    for (int i = n - 1; i > 0; i--) {
        int temp = arr[i];
        arr[i] = arr[0];
        arr[0] = temp;
        heapify(arr, i, 0);
    }
}

} ```

复杂度分析

  • 时间复杂度:O(n log n),这是堆排序的时间复杂度,无论数据集如何,其时间复杂度都是稳定的。
  • 空间复杂度:O(1),堆排序是一种原地排序算法,不需要额外的空间。
  • 稳定性:堆排序不是稳定的排序算法。

总结

本文详细介绍了堆排序的原理和具体实现过程。通过调整完全二叉树节点的方式,可以高效地完成排序任务。希望本文能够帮助读者更好地理解和掌握堆排序。下一部分将对排序算法进行总结。

    本文来源:图灵汇
责任编辑: : 刘思
声明:本文系图灵汇原创稿件,版权属图灵汇所有,未经授权不得转载,已经协议授权的媒体下载使用时须注明"稿件来源:图灵汇",违者将依法追究责任。
    分享
一文排序算法面试了解常考的
    下一篇