在计算机科学的浩瀚算法海洋中,堆排序宛如一颗璀璨的明珠,以其高效和稳定的特性在排序算法领域占据着重要的一席之地。下面我们就来深入探究堆排序的基本思想、原理以及实际应用。
在了解堆排序之前,我们需要先认识一下“堆”这种数据结构。堆是一种特殊的完全二叉树,它分为两种类型:最大堆和最小堆。
最大堆的特点是每个节点的值都大于或等于其子节点的值。也就是说,根节点是整个堆中的最大值。例如,一个最大堆可以表示为以下形式:
9
/ \
7 8
/ \ / \
3 6 4 5
在这个最大堆中,根节点 9 大于其左右子节点 7 和 8,7 大于其子节点 3 和 6,8 大于其子节点 4 和 5。
最小堆则与最大堆相反,每个节点的值都小于或等于其子节点的值,根节点是整个堆中的最小值。例如:
1
/ \
2 3
/ \ / \
4 5 6 7
在这个最小堆中,根节点 1 小于其左右子节点 2 和 3,2 小于其子节点 4 和 5,3 小于其子节点 6 和 7。
堆排序的基本思想是利用堆这种数据结构的特性,通过构建堆和不断调整堆来实现排序。具体步骤可以分为以下两个主要阶段:
首先,将待排序的数组构建成一个堆。如果要进行升序排序,我们通常构建最大堆;如果要进行降序排序,则构建最小堆。以升序排序为例,构建最大堆的过程是从最后一个非叶子节点开始,依次对每个节点进行“调整”操作,使其满足最大堆的性质。
在构建好初始堆后,堆的根节点就是最大值。将根节点与数组的最后一个元素交换位置,此时最大值就被放到了数组的末尾。然后,将剩余的元素重新调整为最大堆,再将新的根节点(即剩余元素中的最大值)与倒数第二个元素交换位置,依此类推,直到整个数组有序。
假设我们有一个待排序的数组 [4, 10, 3, 5, 1]
,下面是构建最大堆的详细过程:
步骤 | 数组状态 | 操作 |
---|---|---|
1 | [4, 10, 3, 5, 1] | 从最后一个非叶子节点开始调整,最后一个非叶子节点的索引为 (n - 2) / 2 ,这里 n = 5 ,所以索引为 1,对应元素 10。由于 10 大于其子节点 5 和 1,无需调整。 |
2 | [4, 10, 3, 5, 1] | 处理索引为 0 的节点 4,4 小于其子节点 10,交换 4 和 10 的位置,得到 [10, 4, 3, 5, 1] 。然后检查交换后的 4 是否满足最大堆性质,4 小于其子节点 5,交换 4 和 5 的位置,得到 [10, 5, 3, 4, 1] 。 |
最终构建好的最大堆对应的数组为 [10, 5, 3, 4, 1]
。
步骤 | 数组状态 | 操作 |
---|---|---|
1 | [10, 5, 3, 4, 1] | 将根节点 10 与最后一个元素 1 交换位置,得到 [1, 5, 3, 4, 10] 。然后将前 4 个元素重新调整为最大堆,得到 [5, 4, 3, 1, 10] 。 |
2 | [5, 4, 3, 1, 10] | 将根节点 5 与倒数第二个元素 1 交换位置,得到 [1, 4, 3, 5, 10] 。再将前 3 个元素重新调整为最大堆,得到 [4, 1, 3, 5, 10] 。 |
3 | [4, 1, 3, 5, 10] | 将根节点 4 与倒数第三个元素 3 交换位置,得到 [3, 1, 4, 5, 10] 。接着将前 2 个元素重新调整为最大堆,得到 [3, 1, 4, 5, 10] 。 |
4 | [3, 1, 4, 5, 10] | 将根节点 3 与倒数第四个元素 1 交换位置,得到 [1, 3, 4, 5, 10] ,此时数组已经有序。 |
def heapify(arr, n, i):
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n and arr[i] < arr[l]:
largest = l
if r < n and arr[largest] < arr[r]:
largest = r
if largest!= i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heapSort(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)
return arr
# 测试
arr = [4, 10, 3, 5, 1]
sorted_arr = heapSort(arr)
print("排序后的数组:", sorted_arr)
堆排序是一种高效的排序算法,它利用堆这种数据结构的特性,通过构建堆和不断调整堆来实现排序。堆排序的时间复杂度稳定在 $O(n log n)$,且不需要额外的存储空间,适用于大规模数据的排序。同时,堆排序的思想还可以应用于其他领域,如优先队列的实现等。通过深入理解堆排序的基本思想和原理,我们可以更好地运用这一算法解决实际问题。