go版本:go1.12 windows/amd64
container目录结构如下:
├─heap
├─heap.go
├─heap_test.go
├─example_intheap_test.g
└─example_pq_test.go
├─list
├─list.go
├─list_test.go
├─example_test.go
└─ring
├─ring.go
├─ring_test.go
└─example_test.go
1. heap包介绍
为所有继承Interface的结构体提供操作,heap是一个根节点为最小值的二叉树(最小堆)
heap包对任意实现了heap接口的类型提供堆操作。
树的最小元素在根部,为index 0.
heap是常用的优先队列的方法,要创建一个优先队列,要实现一个具有负优先级队列,可以通过Less
方法的Heap
接口,如此一来可用Push
添加item,而用Pop
取出队列最高优先级的item。(后面解释
)
type Interface interface {
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}
// sort.Interface如下:
// 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)
}
其中sort.Interface用于排序,heap.Interface中Push和Pop方法分别用于插入和取出元素的操作
heap中的方法:
func Init(h Interface) // h初始化,按最小堆排序,时间复杂度O(n) ,n等于h.Len()
func Pop(h Interface) interface{} // 删除并返回堆h中的最小元素(不影响约束性)。复杂度O(log(n)),n等于h.Len()。该函数等价于Remove(h, 0)。
func Push(h Interface, x interface{}) // 向堆h中插入元素x,并保持堆的约束性。复杂度O(log(n)),n等于h.Len()。
func Remove(h Interface, i int) interface{} // 删除堆中的第i个元素,并保持堆的约束性。复杂度O(log(n)),n等于h.Len()。
func Fix(h Interface, i int) // 在修改第i个元素后,调用本函数修复堆,比删除第i个元素后插入新元素更有效率。复杂度O(log(n)),n等于h.Len()。
2. heap(小顶堆) 构造原理:
小顶堆实际上是一个二叉树,满足:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]规则,即根节点既小于或等于左子树的关键字值,又小于或等于右子树的关键字值。
由此可知, n/2 - 1之前的节点都为父节点,n/2-1之后的节点都为叶子节点,我们可以从 n/2 - 1节点开始,依次与其左叶子节点h[2(n/2-1)+1]和右叶子节点h[2(n/2-1)+2]比较,取三者最小值做为父节点
func Init(h Interface) {
// heapify
n := h.Len()
for i := n/2 - 1; i >= 0; i-- {
down(h, i, n)
}
}
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
}
// 将末尾元素移跟根节点元素交换,然后使用 down 方法来修复堆。最后Pop()取出末尾元素
func Pop(h Interface) interface{} {
n := h.Len() - 1
h.Swap(0, n)
down(h, 0, n)
return h.Pop()
}
// 每次添加新元素,若存在父节点则跟父节点比较,若小于父节点,则交换
func Push(h Interface, x interface{}) {
h.Push(x)
up(h, h.Len()-1)
}
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
}
}
3.使用heap构造优先级队列 (nsq源码的优先级队列实现原理)
思路:通过重写Less()方法,取相反的优先级比较,这样Pop()函数每次取出的即是优先级最高的Item.
// An Item is something we manage in a priority queue.
type Item struct {
value string // The value of the item; arbitrary.
priority int // The priority of the item in the queue.
// The index is needed by update and is maintained by the heap.Interface methods.
index int // The index of the item in the heap.
}
// A PriorityQueue implements heap.Interface and holds Items.
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return pq[i].priority > pq[j].priority
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
item.index = -1 // for safety
*pq = old[0 : n-1]
return item
}
// update modifies the priority and value of an Item in the queue.
func (pq *PriorityQueue) update(item *Item, value string, priority int) {
item.value = value
item.priority = priority
heap.Fix(pq, item.index)
}
// This example creates a PriorityQueue with some items, adds and manipulates an item,
// and then removes the items in priority order.
func Example_priorityQueue() {
// Some items and their priorities.
items := map[string]int{
"banana": 3, "apple": 2, "pear": 4,
}
// Create a priority queue, put the items in it, and
// establish the priority queue (heap) invariants.
pq := make(PriorityQueue, len(items))
i := 0
for value, priority := range items {
pq[i] = &Item{
value: value,
priority: priority,
index: i,
}
i++
}
heap.Init(&pq)
// Insert a new item and then modify its priority.
item := &Item{
value: "orange",
priority: 1,
}
heap.Push(&pq, item)
pq.update(item, item.value, 5)
// Take the items out; they arrive in decreasing priority order.
for pq.Len() > 0 {
item := heap.Pop(&pq).(*Item)
fmt.Printf("%.2d:%s ", item.priority, item.value)
}
// Output:
// 05:orange 04:pear 03:banana 02:apple
}
网友评论