使用堆排序对数组[1, 4, 2, 3]进行排序的过程涉及建立堆(通常是大顶堆)和执行堆调整。以下是详细步骤:
- 建立大顶堆:
将给定数组视为完全二叉树,并从最后一个非叶子节点开始,对每个非叶子节点执行下沉操作(即将较大的值移动到父节点位置,以满足大顶堆的性质)。
最后一个非叶子节点的索引是 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, 4, 2, 3] 转换成了大顶堆 [4, 1, 2, 3]。 排序过程:
现在对大顶堆进行排序。- 首先,取出堆顶元素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, 4, 2, 3]
- 将数组视为完全二叉树,从最后一个非叶子节点开始向上调整,建立大顶堆。
- 最后一个非叶子节点是索引 (4/2) - 1 = 1(对应元素4),它的子节点已经比它小,不需要调整。
- 接下来看索引0的节点(对应元素1),它有两个子节点(对应元素4和2)。将1和4交换,因为4更大。
- 现在大顶堆变成: [4, 1, 2, 3]
开始排序:
- 取出堆顶元素(当前最大值4),将其与堆的最后一个元素交换,得到: [3, 1, 2, | 4](竖线右边的部分表示已经排序好的区域)。
- 此时忽略最后一位,对剩余部分 [3, 1, 2] 重新调整为大顶堆。因为3已经是最大的,所以不需要调整。
- 再次取出堆顶元素(当前最大值3),将其与堆中剩余的最后一个元素交换,得到: [2, 1, | 3, 4]。
- 忽略最后两位,对剩余部分 [2, 1] 重新调整为大顶堆。同样,因为2已经是最大的,所以不需要调整。
- 最后取出堆顶元素(当前最大值2),将其与剩余的最后一个元素交换,得到: [1, | 2, 3, 4]。
排序完成:
- 最后,堆中只剩下一个元素(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);
- heapify 函数用于将树调整为大顶堆的过程,它将树的根节点与其子树中的较大节点交换,然后递归地调整交换后的子树。
- heapSort 函数首先通过 heapify 函数从最后一个非叶子节点开始创建一个大顶堆。然后,它重复从堆顶移除最大元素并调整堆的过程,直到排序完成。
- 在 heapSort 函数中,通过两个循环完成排序:第一个循环建立大顶堆,第二个循环进行实际的排序。
- 最后,打印原始数组和排序后的数组。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。