golang container/heap源码阅读笔记
1. 源码解析
type Interface interface {
sort.Interface
Push(x interface{
}) // add x as element Len() 将x作为第len()个元素加入堆中
Pop() interface{
} // remove and return element Len() - 1.
}
首先定义heap的接口,其中 sort.Interface
包括一下三个接口,任何类型,只要实现了这五个接口,就是一个heap
Interface interface {
// Len is the number of elements in the collection. 集合内的元素个数
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
// 返会索引为i的元素是否应该在j的前面
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
堆的逻辑结构是满二叉树,堆的常用的存储结构有顺序存储、链表存储,go的官方源码采用的是顺序结构。
以小根堆为例说明
down()函数是从上到下调整的过程,就是从节点i0开始,将其与子节点中较小的节点交换,直到i最后的位置为叶节点或者以i为父节点的子树满足小根堆的要求。
如果发生了交换,i就比i0大,否则 i等于i0 返回值 i>i0表示是否发生的交换
func down(h Interface, i0, n int) bool {
i := i0
for {
j1 := 2*i + 1
if j1 >= n || j1 < 0 {
// j1 < 0 after int overflow
break
}
// 如果j1大于n,说明索引i没有孩子节点,说明它本身就是孩子节点,此时已经
j := j1 // left child
if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
// 判断右孩子节点是否存在,且 如果小于j1,就将j2赋值给j
j = j2 // = 2*i + 2 // right child
}
// 此时得到的j为i的两个孩子节点较小的索引
if !h.Less(j, i) {
// 判断 孩子节点与父节点是否需要交换,如果不需要,说明已经满足堆的要求(小根或者大根)
break
}
h.Swap(i, j)
// 交换父节点和子节点
i = j
}
return i > i0
}
up()函数是从下向上调整的过程,将j节点与其父节点比较,若满足小根堆的根值小于孩子值,就交换。
注意,up()函数不考虑兄弟节点,只要自己比父亲小,就可以交换。
func up(h Interface, j int) {
for {
i := (j - 1) / 2 // parent
if i == j || !h.Less(j, i) {
break
}
h.Swap(i, j)
j = i
}
}
结合上图 init()函数就是从非叶节点开始,逆序调整节点,构建堆heap
// The complexity is O(n) where n = h.Len().
func Init(h Interface