基本思想
借用「堆结构」所设计的排序算法。将数组转化为大顶堆,重复从大顶堆中取出数值最大的节点,并让剩余的堆结构继续维持大顶堆性质。
算法步骤
-
构建初始大顶堆:
- 定义一个数组实现的堆结构,将原始数组的元素依次存入堆结构的数组中(初始顺序不变)。
- 从数组的中间位置开始,从右至左,依次通过「下移调整」将数组转换为一个大顶堆。
-
交换元素,调整堆:
- 交换堆顶元素(第 1 个元素)与末尾(最后 1 个元素)的位置,交换完成后,堆的长度减 1。
- 交换元素之后,由于堆顶元素发生了改变,需要从根节点开始,对当前堆进行「下移调整」,使其保持堆的特性。
-
重复交换和调整堆:
- 重复第 2 步,直到堆的大小为 1 时,此时大顶堆的数组已经完全有序。
适用场景
堆排序是一种比较高效的排序算法,具有以下适用场景:
大规模数据排序、内存受限环境、动态数据排序、求第 K 大(小)元素
排序稳定性
在进行「下移调整」时,相等元素的相对位置可能会发生变化。因此,堆排序是一种 不稳定排序算法。
代码实现(golang)
func heapSort(arr []int) []int {
n := len(arr)
// 构建最大堆
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
// 一个个取出堆顶元素并调整堆
for i := n - 1; i >= 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
}
return arr
}
func heapify(arr []int, n, i int) {
largest := i
left := 2*i + 1
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)
}
}
func main() {
arr := []int{12, 11, 13, 5, 6, 7}
sortedArr := heapSort(arr)
fmt.Println(sortedArr)
}