- 在顶部构建最大元素的堆
(1)从最后一个拥有子节点的父节点开始往上实现父元素更大堆属性 - 最大的元素放置末尾
(1)将未排序队列中的最大元素放置末尾
(2)使未排序队列实现父元素更大堆属性
实现父元素更大堆属性的具体步骤如下:
- 循环父节点下的所有子节点
- 判断父节点是否存在子节点,若不存在子节点退出循环
- 判断两个子节点哪个更大,选择大的那一个子节点
- 判断父节点是否大于子节点,若大于则退出循环
- 互换父子节点
- 将子节点设为父节点重新循环
该实现其实就是golang标准库下排序中的堆排序实现,只不过由于在Sort中堆排序只是其排序逻辑中的一部分,因此具有一些其他与堆排序无关的属性,在此将其删除。
func siftDown(array []int, lo, hi int) { rootIndex := lo //1.循环对比是否有比父节点大的子节点,当父节点为叶子节点或比子节点大时退出循环 for { childIndex := 2*rootIndex + 1 //2.如果子节点下标大于堆的最大长度,则不存在该子节点退出循环 if childIndex >= hi { break } //3.如果子节点的下一个兄弟节点大于等于该子节点,则定位下一个子节点下标 if childIndex+1 < hi && array[childIndex] <= array[childIndex+1] { childIndex++ } //4.如果父节点大于子节点则退出循环 if array[rootIndex] > array[childIndex] { break } //5.互换父子节点 array[rootIndex], array[childIndex] = array[childIndex], array[rootIndex] rootIndex = childIndex //6.将子节点设为父节点 }}func HeapSort(array []int) { lo := 0 hi := len(array) // 1.构建大顶堆,从最后一个拥有子节点的父节点开始往上循环 for i := (hi - 1) / 2; i >= 0; i-- { //在array[i,len)上实现堆属性 siftDown(array, i, hi) } // 2.循环将当前序列中最大的元素放置末尾 for i := hi - 1; i >= 0; i-- { //将array[0]与array[i]互换,即将最大元素放置末尾 array[0], array[i] = array[i], array[0] //在array[0,i)上实现堆属性,即找出最大元素 siftDown(array, lo, i) }}
仅看代码的情况下如果你对堆的数据结构不怎么了解或许你还是会比较懵,因此我就用图来举个例子,剩下的过程你自己试验一下看一遍流程立马就懂了。
现调用HeapSort函数并传入一个int切片
HeapSort([]int{5, 4, 6, 7, 0, 3, 4, 8, 2, 9})
此时切片[]int{5, 4, 6, 7, 0, 3, 4, 8, 2, 9}转换为堆的效果就是
当程序运行至
// 1.构建大顶堆,从最后一个拥有子节点的父节点开始往上循环 for i := (hi - 1) / 2; i >= 0; i-- { //在array[i,len)上实现堆属性 siftDown(array, i, hi) }
siftDown(array,4,10)
childIndex := 2*rootIndex + 1
2*rootIndex + 1
if childIndex >= hi { break}
9小于堆的最大长度10,则存在该子节点,循环继续。
if childIndex+1 < hi && array[childIndex] <= array[childIndex+1] { childIndex++}
9+1并不小于最大长度10,不存在下一个兄弟节点,跳过if判断,循环继续。
if array[rootIndex] > array[childIndex] { break}array[rootIndex], array[childIndex] = array[childIndex], array[rootIndex]rootIndex = childIndex //6.将子节点设为父节点
4<9,父节点并不大于子节点,因此将其互换并将子节点设为父节点继续循环
childIndex := 2*rootIndex + 1if childIndex >= hi { break}
下标9的子节点下标为19,大于堆的长度10,不存在该子节点退出循环。
array此时为[5, 4, 6, 7, 9, 3, 4, 8, 2, 0]
逼逼赖赖这么多不如放张图:
图实在是占地方了,后续循环我就不放图了,给你们个结果切片[]int自己转换,看一看就懂了。
初始值 [5 4 6 7 0 3 4 8 2 9]
调用siftDown(array, 4 , 10 )
交换后结果 [5 4 6 7 9 3 4 8 2 0] 继续循环
rootIndex: 9 没有子节点退出循环
调用siftDown(array, 3 , 10 )
交换后结果 [5 4 6 8 9 3 4 7 2 0] 继续循环
rootIndex: 7 没有子节点退出循环
调用siftDown(array, 2 , 10 )
child兄弟节点更大,选择该兄弟节点childIndex: 6
父节点比子节点大退出循环
调用siftDown(array, 1 , 10 )
child兄弟节点更大,选择该兄弟节点childIndex: 4
交换后结果 [5 9 6 8 4 3 4 7 2 0] 继续循环
父节点比子节点大退出循环
调用siftDown(array, 0 , 10 )
交换后结果 [9 5 6 8 4 3 4 7 2 0] 继续循环
交换后结果 [9 8 6 5 4 3 4 7 2 0] 继续循环
交换后结果 [9 8 6 7 4 3 4 5 2 0] 继续循环
rootIndex: 7 没有子节点退出循环
最后获取得的 [9 8 6 7 4 3 4 5 2 0] 就是大顶堆,父节点皆比子节点大。
排序我就不举例了,都是一个意思。如果还看不懂就把切片换成堆结构你就明白了。
结束
以后有机会可以把sort包拿出来说说。