堆(heap)是实际中比较常用的数据结构,Go标准库container/heap中实现该数据结构,只需根据实际情况进行接口实现即可。
堆的概念
特性:
- 堆是完全二叉树
- 堆中每个节点的值都**大于等于(或小于等于)**其子树中每个节点的值
分类:
- 小顶堆:堆中每个节点的值都必须小于等于其子树中每个节点的值
- 大顶堆:堆中每个节点的值都必须大于等于其子树中每个节点的值
Go的堆实现
基于Go的标准库container/heap,只需进行接口实现即可
需实现的接口为:
// The Interface type describes the requirements
// for a type using the routines in this package.
// Any type that implements it may be used as a
// min-heap with the following invariants (established after
// Init has been called or if the data is empty or sorted):
//
// !h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
//
// Note that Push and Pop in this interface are for package heap's
// implementation to call. To add and remove things from the heap,
// use heap.Push and heap.Pop.
type Interface interface { // 该接口组合了另外的sort.Interface接口,所以也要实现里面的方法
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}
其中sort.Interface接口方法为
package sort
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type 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.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
因此基于container/heap实现堆结构只需实现上述五个接口即可
代码实现
小顶堆实现代码如下,实现大小顶堆的区别在Less(i, j int) bool 接口
import (
"container/heap"
"fmt"
)
type IntHeap []int
func main() {
h := &IntHeap{2, 1, 5, 6, 4, 3, 7, 9, 8, 0} // 创建slice
heap.Init(h) // 将数组切片进行堆化
fmt.Println(*h) // [0 1 3 6 2 5 7 9 8 4] 由Less方法可控制小顶堆
fmt.Println(heap.Pop(h)) // 调用pop 0 返回移除的顶部最小元素
heap.Push(h, 6) // 调用push [1 2 3 6 4 5 7 9 8] 添加一个元素进入堆中进行堆化
fmt.Println("new: ", *h) // [1 2 3 6 4 5 7 9 8 6]
for len(*h) > 0 { // 持续推出顶部最小元素
fmt.Printf("%d \n ", heap.Pop(h))
}
}
func (h IntHeap) Len() int { return len(h) }
// 这里决定 大小顶堆 现在是小顶堆。大顶堆变成 h[i]>h[j] 即可。
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
fmt.Println("old: ", old) // [1 2 3 6 4 5 7 9 8 0] 将顶小堆元素与最后一个元素交换位置,在进行堆排序的结果
x := old[n-1]
*h = old[0 : n-1]
fmt.Println(*h) // [1 2 3 6 4 5 7 9 8]
return x
}
func (h *IntHeap) Push(x interface{}) { // 绑定push方法,插入新元素
*h = append(*h, x.(int))
}
对于Int类型的小顶堆,可通过匿名结构体嵌套来简化实现接口的个数
// 小顶堆
type hp struct{
sort.IntSlice
}
func (h *hp) Push(v interface{}){
h.IntSlice = append(h.IntSlice, v.(int))
}
func (h *hp) Pop() interface{} {
a := h.IntSlice
v := a[len(a)-1]
h.IntSlice = a[:len(a)-1]
return v
}