堆排序算法的执行过程(堆排序的实现过程)

堆排序(Heap Sort)是一种基于二叉堆(Binary Heap)数据结构的比较排序算法。它的执行过程可以分为两个主要阶段:建堆(Heapify)排序(Sorting)。下面详细解释堆排序的执行过程。


1. 堆的基本概念

  • 二叉堆:一种完全二叉树,满足堆性质:最大堆:每个父节点的值大于或等于其子节点的值。最小堆:每个父节点的值小于或等于其子节点的值。
  • 堆的存储:通常用数组表示,对于节点 i:左子节点:2i + 1右子节点:2i + 2父节点:(i - 1)/2

2. 堆排序的步骤

阶段一:建堆(Heapify)

将无序数组构建成一个堆(通常为最大堆)。

  1. 从最后一个非叶子节点开始调整
  2. 最后一个非叶子节点的索引为 n/2 - 1(n 是数组长度)。
  3. 从该节点开始,依次向前调整每个子树,使其满足堆性质。
  4. 调整方法(Sift Down)
  5. 比较当前节点与其左右子节点的值。
  6. 如果当前节点小于某个子节点(最大堆),则交换它们。
  7. 递归调整被交换的子节点,直到当前节点满足堆性质。

阶段二:排序(Sorting)

将堆顶元素(最大值)与堆末尾元素交换,并逐步缩小堆的范围。

  1. 交换堆顶与末尾元素
  2. 将堆顶(最大值)与当前堆的最后一个元素交换。
  3. 此时,最大值被放到数组末尾。
  4. 调整剩余堆
  5. 对新的堆顶元素执行 Sift Down,重新满足堆性质。
  6. 堆的大小减 1(排除已排序的部分)。
  7. 重复直到堆为空
  8. 重复交换和调整,直到所有元素有序。

3. 示例演示

假设对数组 [4, 10, 3, 5, 1] 进行升序排序(使用最大堆)。

步骤 1:建堆

初始数组:[4, 10, 3, 5, 1]
完全二叉树表示:

        4
       / \
      10  3
     / \
    5   1
  1. 最后一个非叶子节点是 10(索引 1)。调整 10:它比子节点 5 和 1 大,无需交换。
  2. 调整 4(索引 0):4 比左子节点 10 小,交换 4 和 10。子树 [4, 5, 1] 继续调整:4 比 5 小,交换 4 和 5。最终堆:
        10
       / \
      5   3
     / \
    4   1

步骤 2:排序

  1. 交换堆顶 10 和末尾 1:数组变为 [1, 5, 3, 4, 10]。堆范围缩小为 [1, 5, 3, 4]。
  2. 调整 1:1 与 5 交换,再与 4 交换:堆变为 [5, 4, 3, 1]。
  3. 交换堆顶 5 和末尾 1:数组变为 [1, 4, 3, 5, 10]。堆范围缩小为 [1, 4, 3]。
  4. 重复上述过程,最终得到有序数组 [1, 3, 4, 5, 10]。

4. 时间复杂度

  • 建堆:O(n)。
  • 排序:每次 Sift Down 是 O(log n),共 n-1 次,因此为 O(n log n)。
  • 总时间复杂度:O(n log n)。
  • 空间复杂度:O(1)(原地排序)。

5. 代码实现(Python)

def heapify(arr, n, i):
    largest = i  # 初始化当前节点为最大值
    left = 2 * i + 1
    right = 2 * i + 2

    # 比较左子节点
    if left < n and arr[left] > arr[largest]:
        largest = left

    # 比较右子节点
    if right < n and arr[right] > arr[largest]:
        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[0], arr[i] = arr[i], arr[0]  # 交换堆顶和末尾
        heapify(arr, i, 0)  # 调整剩余堆

# 测试
arr = [4, 10, 3, 5, 1]
heap_sort(arr)
print("排序后数组:", arr)  # 输出: [1, 3, 4, 5, 10]

堆排序(Heap Sort)是一种基于二叉堆(Binary Heap)数据结构的比较排序算法。它的执行过程可以分为两个主要阶段:建堆(Heapify)排序(Sorting)。下面详细解释堆排序的执行过程。


1. 堆的基本概念

  • 二叉堆:一种完全二叉树,满足堆性质:最大堆:每个父节点的值大于或等于其子节点的值。最小堆:每个父节点的值小于或等于其子节点的值。
  • 堆的存储:通常用数组表示,对于节点 i:左子节点:2i + 1右子节点:2i + 2父节点:(i - 1)/2

2. 堆排序的步骤

阶段一:建堆(Heapify)

将无序数组构建成一个堆(通常为最大堆)。

  1. 从最后一个非叶子节点开始调整
  2. 最后一个非叶子节点的索引为 n/2 - 1(n 是数组长度)。
  3. 从该节点开始,依次向前调整每个子树,使其满足堆性质。
  4. 调整方法(Sift Down)
  5. 比较当前节点与其左右子节点的值。
  6. 如果当前节点小于某个子节点(最大堆),则交换它们。
  7. 递归调整被交换的子节点,直到当前节点满足堆性质。

阶段二:排序(Sorting)

将堆顶元素(最大值)与堆末尾元素交换,并逐步缩小堆的范围。

  1. 交换堆顶与末尾元素
  2. 将堆顶(最大值)与当前堆的最后一个元素交换。
  3. 此时,最大值被放到数组末尾。
  4. 调整剩余堆
  5. 对新的堆顶元素执行 Sift Down,重新满足堆性质。
  6. 堆的大小减 1(排除已排序的部分)。
  7. 重复直到堆为空
  8. 重复交换和调整,直到所有元素有序。

3. 示例演示

假设对数组 [4, 10, 3, 5, 1] 进行升序排序(使用最大堆)。

步骤 1:建堆

初始数组:[4, 10, 3, 5, 1]
完全二叉树表示:

复制

4 / \ 10 3 / \ 5 1

  1. 最后一个非叶子节点是 10(索引 1)。调整 10:它比子节点 5 和 1 大,无需交换。
  2. 调整 4(索引 0):4 比左子节点 10 小,交换 4 和 10。子树 [4, 5, 1] 继续调整:4 比 5 小,交换 4 和 5。最终堆:
复制

10 / \ 5 3 / \ 4 1

步骤 2:排序

  1. 交换堆顶 10 和末尾 1:数组变为 [1, 5, 3, 4, 10]。堆范围缩小为 [1, 5, 3, 4]。
  2. 调整 1:1 与 5 交换,再与 4 交换:堆变为 [5, 4, 3, 1]。
  3. 交换堆顶 5 和末尾 1:数组变为 [1, 4, 3, 5, 10]。堆范围缩小为 [1, 4, 3]。
  4. 重复上述过程,最终得到有序数组 [1, 3, 4, 5, 10]。

4. 时间复杂度

  • 建堆:O(n)。
  • 排序:每次 Sift Down 是 O(log n),共 n-1 次,因此为 O(n log n)。
  • 总时间复杂度:O(n log n)。
  • 空间复杂度:O(1)(原地排序)。

5. 代码实现(Python)

python

复制

def heapify(arr, n, i):
    largest = i  # 初始化当前节点为最大值
    left = 2 * i + 1
    right = 2 * i + 2

    # 比较左子节点
    if left < n and arr[left] > arr[largest]:
        largest = left

    # 比较右子节点
    if right < n and arr[right] > arr[largest]:
        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[0], arr[i] = arr[i], arr[0]  # 交换堆顶和末尾
        heapify(arr, i, 0)  # 调整剩余堆

# 测试
arr = [4, 10, 3, 5, 1]
heap_sort(arr)
print("排序后数组:", arr)  # 输出: [1, 3, 4, 5, 10]

6. 关键点总结

  1. 建堆:从下往上调整,确保每个子树满足堆性质。
  2. 排序:每次将堆顶(最大值)交换到末尾,并调整剩余堆。
  3. 稳定性:堆排序是不稳定的排序算法(相同元素的相对位置可能改变)。
  4. 适用场景:适合数据量大的情况,但不如快速排序和归并排序常用(因缓存不友好)。


原文链接:,转发请注明来源!