Golang标准库学习—container/heap

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))。

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。(标准库里好像有例子)

堆的用处包括使用频率都还是很大的,具体的实现原理不再赘述,但还是要牢牢把握啊。

记录每天解决的一点小问题,积累起来就能解决大问题。

猜你喜欢

转载自blog.csdn.net/Lazyboy_/article/details/88257912
今日推荐