如图所示,排序算法主要分为两大类:比较型排序和非比较型排序。根据功能的不同,排序算法又可以细分为交换类、选择类、归并类、插入类和计数类。之前的文章已经介绍了交换排序中的快速排序、归并排序中的二路归并排序以及非比较排序中的基数排序。接下来,我们将重点讨论选择排序中的堆排序。
堆可以视为一种特殊的完全二叉树,其节点满足一定的条件:子节点的键值或索引总是小于(或大于)父节点。因此,堆可分为最大堆和最小堆。
完全二叉树是一种特殊的二叉树,其中除了最后一层可能不满之外,其余各层都是满的。满二叉树是每一层都填满节点的二叉树,一个深度为m的满二叉树有(2^m - 1)个节点,且第m层有(2^{m-1})个节点。如下图所示:
下面以最大堆为例介绍堆排序的具体过程。最小堆的处理方式类似。堆排序的核心在于节点的调整,以确保每个节点都大于其子节点(对于最大堆而言)。
假设初始待排序序列为 [1, 8, 9, 3, 4, 6, 7]。
构建初始堆:
调整堆结构:将根节点与最后一个节点交换位置,得到无序序列 [1, 8, 7, 3, 4, 6] 和有序序列 [9]。
继续调整:重复上述步骤,最终得到有序序列 [1, 3, 4, 6, 7, 8, 9]。
```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 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);
}
}
} ```
本文详细介绍了堆排序的原理和具体实现过程。通过调整完全二叉树节点的方式,可以高效地完成排序任务。希望本文能够帮助读者更好地理解和掌握堆排序。下一部分将对排序算法进行总结。