container包中有三个数据结构:heap(堆)、list(链表)、ring(环)
Package heap
import "container/heap"
heap包提供了对任意实现了heap.Interface接口的类型的堆操作。最小堆是具有“每个节点都是以其为根的子树中最小值”属性的树。最大堆相反。
树的最小元素为其根元素,索引0的位置。最大堆相反。
heap是常用的实现优先队列的方法。要创建一个优先队列,实现一个具有使用(负的)优先级作为比较的依据的Less方法的Heap接口,如此一来可用Push添加项目而用Pop取出队列最高优先级的项目。
type Interface
这里不像list声明类型,而是直接作为接口令外部使用
type Interface interface { sort.Interface Push(x interface{}) // add x as element Len() Pop() interface{} // remove and return element Len() - 1. }
任何实现了本接口的类型都可以用于构建最小堆。最小堆可以通过heap.Init建立,数据是递增顺序或者空的话也是最小堆。
其中,这个堆结构组合了sort.Interface,所以需要实现Len(), Less(), Swap()三个方法。
注意接口的Push和Pop方法是供heap包调用的,使用heap.Push和heap.Pop来向一个堆添加或者删除元素。
heap对外提供五个方法
首先是Init方法。一个堆在使用任何堆操作之前应先初始化。Init函数对于堆的约束性是幂等的(多次执行无意义),并可能在任何时候堆的约束性被破坏时被调用。函数复杂度为O(n),其中n等于h.Len()。其中存在down内部方法,后面说。
func Init(h Interface) { // heapify n := h.Len() for i := n/2 - 1; i >= 0; i-- { down(h, i, n) } }
接着是向堆中添加元素的Push方法。向堆h中插入元素x,并保持堆的约束性。复杂度O(log(n)),其中n等于h.Len()。
func Push(h Interface, x interface{}) { h.Push(x) up(h, h.Len()-1) }
还有从堆中删除根元素的Pop方法。删除并返回堆h中的最小元素(不影响约束性)。复杂度O(log(n))。
扫描二维码关注公众号,回复: 5640924 查看本文章func Pop(h Interface) interface{} { n := h.Len() - 1 h.Swap(0, n) down(h, 0, n) return h.Pop() }
然后是从堆中删除第i各元素的Remov方法。删除堆中的第i个元素,并保持堆的约束性。复杂度O(log(n))。
func Remove(h Interface, i int) interface{} { n := h.Len() - 1 if n != i { h.Swap(i, n) if !down(h, i, n) { up(h, i) } } return h.Pop() }
最后是调整堆的Fix方法。在修改第i个元素后,调用本函数修复堆,比删除第i个元素后插入新元素更有效率。复杂度O(log(n))。
func Fix(h Interface, i int) { if !down(h, i, h.Len()) { up(h, i) } }
heap内部存在两个方法来实现对外的五个方法
两个方法分别实现堆的上下调整,单独调用只能保证调用节点保持约束性,不能保证其它节点。
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 } } 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 } j := j1 // left child if j2 := j1 + 1; j2 < n && h.Less(j2, j1) { j = j2 // = 2*i + 2 // right child } if !h.Less(j, i) { break } h.Swap(i, j) i = j } return i > i0 }
这里不再做实例了,只要实现相应方法就可以使用堆了。好像golang里也没有像C++那样做了priority_queue优先队列的库实现,用的时候自己做一个8。(标准库里好像有例子)
堆的用处包括使用频率都还是很大的,具体的实现原理不再赘述,但还是要牢牢把握啊。
记录每天解决的一点小问题,积累起来就能解决大问题。