一、堆的结构
堆是一种数据结构,完全二叉树
二、堆的性质
堆分为大顶堆,小顶堆
大顶堆:根节点为整个堆的最大值。每个节点的值都不小于其孩子节点的值。
小顶堆:根节点为整个堆的最小值。每个节点的值都不大于其孩子节点的值
如果把堆从上到下、从左到右映射为一个数组。如图:
有如下性质:
大顶堆:arr[i] >= arr[2i]+1 && arr[i] >= arr[2i]+2
小顶堆:arr[i]
三、堆排序思想
1、将要排序的序列构建成一个大顶堆。
1.1、构建堆之前需要先找到堆的所有非叶子节点。但堆的本质是一个完全二叉树。因此堆的第一个叶子节点的下标为:数组长度/2-1(这里需要向下取整)。则堆的所有叶子节点的下标是[0,1....,leng/2-1]
1.2 从最后一个叶子节点的值开始遍历,去比较每个节点和其孩子节点的大小、并调整顺序。直到根节点。如大顶堆:则将节点同孩子节点中的最大值比较。如果孩子节点的值大于该节点,则交换值。否则不叫交换。
2、将堆顶的值与数组末尾交换、将最大值放在末尾
3、重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
代码实现:
func heapSort(arr []int){
length := len(arr)
if length <= 1{
return
}
//找到第一个非叶子节点length/2 - 1。开始调整构建大顶堆
for i:=length/2-1;i>=0;i--{
getArrayToHeap(arr,i,length)
}
//每次交换堆顶和数组尾部的值。同时再次调整堆的顺畅
for i:=length-1;i>=0;i--{
arr[0],arr[i] = arr[i],arr[0]
//此时只需要简单的从根开始调整,因为交换顺序后除了堆顶,其他节点的顺序并未改变,因此不需要再次构建堆
getArrayToHeap(arr,0,i)
}
}
//堆调整
func getArrayToHeap(arr []int, index,length int){
if length <= 1 || index < 0 {
return
}
//左孩子节点的下标
leftIndex := index<<1 + 1
//右孩子节点的下标
rightIndex := index<<1 + 2
//初始设置右孩子节点的下标为最大值,因为有可能没有左孩子节点
maxIndex := rightIndex
if leftIndex < length && rightIndex < length && arr[rightIndex] < arr[leftIndex]{
maxIndex = leftIndex
}
if maxIndex < length && arr[maxIndex] > arr[index]{
//调整孩子节点和该节点顺序
arr[maxIndex], arr[index] = arr[index], arr[maxIndex]
//调整后需要将孩子节点的顺序再次调整 来保证堆的顺序
getArrayToHeap(arr,maxIndex,length)
}
return
}