使用堆排序对数组[1, 4, 2, 3]进行排序的过程涉及建立堆(通常是大顶堆)和执行堆调整。以下是详细步骤:

  1. 建立大顶堆:
    将给定数组视为完全二叉树,并从最后一个非叶子节点开始,对每个非叶子节点执行下沉操作(即将较大的值移动到父节点位置,以满足大顶堆的性质)。

最后一个非叶子节点的索引是 n/2 - 1,其中 n 是数组的长度。在我们的例子中,数组的长度为4,所以最后一个非叶子节点的索引是 (4/2) - 1 = 1。

  • 开始时,数组是 [1, 4, 2, 3]。
  • 从索引1的节点(值为4)开始,它已经是最大的,不需要调整。
  • 接下来看索引0的节点(值为1),它有两个子节点(值为4和2)。4是最大的,所以我们将1和4交换。
  • 交换后,数组变为 [4, 1, 2, 3]。

2.排序过程:

一旦建立了大顶堆,就可以开始排序过程。每次从堆顶(即数组的第一个元素)取出最大值,将其与堆的最后一个元素交换,然后减小堆的大小,并重新调整新堆的结构。
  • 首先,取出堆顶元素4,将其与最后一个元素3交换,此时堆的大小减1,数组变为 [3, 1, 2, 4]。
  • 现在,忽略数组中的最后一个元素(4,已经是排序好的),对剩余的
  • 比较索引0的元素3和它的两个子节点1和2,最大的是3,所以它保持在顶部,不需要交换。

剩下的堆已经是一个有效的大顶堆 [3, 1, 2],4已经放到最终位置。

  • 现在,再次取出堆顶元素3,将其与堆中的最后一个元素2交换,此时堆的大小再减1,数组变为 [2, 1, 3, 4]。
  • 忽略最后两个元素(3和4),只对前两个元素重建大顶堆。从索引0开始,子节点索引1的元素是1,因为2大于1,所以不需要交换。

剩下的堆 [2, 1] 依然是有效的大顶堆,3和4已经是排序好的。

  • 最后,取出堆顶元素2,将其与堆中的最后一个元素1交换,现在堆的大小再减1,数组变为 [1, 2, 3, 4]。
  • 此时,只剩下一个元素,不需要再做堆调整。

最终,数组 [1, 2, 3, 4] 就是排序好的结果。堆排序过程完成。

总结一下,堆排序的过程是:

  1. 构建一个大顶堆。
    我们已经将原始数组 [1, 4, 2, 3] 转换成了大顶堆 [4, 1, 2, 3]。
  2. 排序过程:
    现在对大顶堆进行排序。

    • 首先,取出堆顶元素4,将其与最后一个元素3交换,此时堆的大小减1,数组变为 [3, 1, 2, 4]。已经从堆中移除的元素4是当前最大值,它将保持在数组末尾。
    • 现在需要对剩余的三个元素 [3, 1, 2] 重建大顶堆。从索引0开始下沉操作,比较索引0的元素3和它的两个子节点1和2。3已经是最大的,所以它保持在顶部,不需要交换。大顶堆仍然是 [3, 1, 2]。
    • 再次取出堆顶元素3,将其与最后一个元素2交换,堆的大小减1,数组变为 [2, 1, 3, 4]。
    • 对剩余的两个元素 [2, 1] 重建大顶堆。从索引0开始下沉操作,2比1大,所以不需要交换。大顶堆仍然是 [2, 1]。
    • 最后,取出堆顶元素2,将其与最后一个元素1交换,堆的大小减1,数组变为 [1, 2, 3, 4]。此时,堆中只剩下一个元素1,不需要再进行调整。

最终得到的数组 [1, 2, 3, 4] 是完全排序好的,堆排序过程结束。下面是整个堆排序过程的逐步解释:

  1. 初始化大顶堆:

    • 初始数组: [1, 4, 2, 3]
    • 将数组视为完全二叉树,从最后一个非叶子节点开始向上调整,建立大顶堆。
    • 最后一个非叶子节点是索引 (4/2) - 1 = 1(对应元素4),它的子节点已经比它小,不需要调整。
    • 接下来看索引0的节点(对应元素1),它有两个子节点(对应元素4和2)。将1和4交换,因为4更大。
    • 现在大顶堆变成: [4, 1, 2, 3]
  2. 开始排序:

    • 取出堆顶元素(当前最大值4),将其与堆的最后一个元素交换,得到: [3, 1, 2, | 4](竖线右边的部分表示已经排序好的区域)。
    • 此时忽略最后一位,对剩余部分 [3, 1, 2] 重新调整为大顶堆。因为3已经是最大的,所以不需要调整。
    • 再次取出堆顶元素(当前最大值3),将其与堆中剩余的最后一个元素交换,得到: [2, 1, | 3, 4]。
    • 忽略最后两位,对剩余部分 [2, 1] 重新调整为大顶堆。同样,因为2已经是最大的,所以不需要调整。
    • 最后取出堆顶元素(当前最大值2),将其与剩余的最后一个元素交换,得到: [1, | 2, 3, 4]。
  3. 排序完成:

    • 最后,堆中只剩下一个元素(1),它已经是最小的,无需再调整。
    • 完整的排序结果是 [1, 2, 3, 4]。

    通过这个过程,我们可以看出,堆排序是通过不断地建立大顶堆,然后将堆顶元素(当前最大)移动到数组的末尾,再对剩余的部分重新建立大顶堆,重复这个过程,直到整个数组排序完成。

代码示例:

function heapify(arr: number[], n: number, i: number): void {
  let largest = i;
  let left = 2 * i + 1;
  let right = 2 * i + 2;

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

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

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

function heapSort(arr: number[]): number[] {
  let n = arr.length;

  // Build max heap
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    heapify(arr, n, i);
  }

  // Heap sort
  for (let i = n - 1; i > 0; i--) {
    // Move current root to end
    [arr[0], arr[i]] = [arr[i], arr[0]];
    heapify(arr, i, 0);
  }

  return arr;
}

// Example array
let arr = [1, 4, 2, 3];
console.log("Original array:", arr);

// Sorting the array using heapSort function
arr = heapSort(arr);
console.log("Sorted array:", arr);
  1. heapify 函数用于将树调整为大顶堆的过程,它将树的根节点与其子树中的较大节点交换,然后递归地调整交换后的子树。
  2. heapSort 函数首先通过 heapify 函数从最后一个非叶子节点开始创建一个大顶堆。然后,它重复从堆顶移除最大元素并调整堆的过程,直到排序完成。
  3. 在 heapSort 函数中,通过两个循环完成排序:第一个循环建立大顶堆,第二个循环进行实际的排序。
  4. 最后,打印原始数组和排序后的数组。

Juven
127 声望4 粉丝