概述
heap包提供了對任意類型(實作了heap.Interface接口)的堆操作。(最小)堆是具有“每個節點都是以其為根的子樹中最小值”屬性的樹。
樹的最小元素為其根元素,索引0的位置。
heap是常用的實作優先隊列的方法。要建立一個優先隊列,實作一個具有使用(負的)優先級作為比較的依據的Less方法的Heap接口,如此一來可用Push添加項目而用Pop取出隊列最高優先級的項目。
這是文檔中的解釋。
(container/heap 容器資料結構heap
heap的實作使用到了小根堆,下面先對堆做個簡單說明
1. 堆概念
堆是一種經過排序的完全二叉樹,其中任一非終端節點的資料值均不大于(或不小于)其左孩子和右孩子節點的值。
最大堆和最小堆是二叉堆的兩種形式。
最大堆:根結點的鍵值是所有堆結點鍵值中最大者。
最小堆:根結點的鍵值是所有堆結點鍵值中最小者。
2. heap
樹的最小元素在根部,為index 0.
heap包對任意實作了heap接口的類型提供堆操作。
heap是常用的實作優先隊列的方法。要建立一個優先隊列,實作一個具有使用(負的)優先級作為比較的依據的Less方法的Heap接口,如此一來可用Push添加項目而用Pop取出隊列最高優先級的項目。
類型接口
heap包中核心是heap.Interface接口, 堆的基礎存儲是一個樹形結構,可以用數組或是連結清單實作,通過heap的函數,可以建立堆并在堆上進行操作。
heap.Interface接口源碼:
type Interface interface {
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}
sort.Interface接口源碼
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)
}
在實作了這些接口之後,就可以被heap包提供的各個函數進行操作,進而實作一個堆。
根據上面interface的定義,可以看出這個堆結構繼承自sort.Interface, 而sort.Interface,需要實作三個方法:Len(), Less() , Swap() 。
同僚還需要實作堆接口定義的兩個方法:Push(x interface{}) / Pop() interface{}, 是以我們要想使用heap定義一個堆, 隻需要定義實作了這五個方法結構就可以了。
注意
接口的Push和Pop方法是供heap包調用的,請使用heap.Push和heap.Pop來向一個堆添加或者删除元素。
成員函數
heap包中提供了幾個最基本的堆操作函數,包括Init,Fix,Push,Pop和Remove (其中up, down函數為非導出函數)。這些函數都通過調用前面實作接口裡的方法,對堆進行操作。
Init
func Init(h Interface)
一個堆在使用任何堆操作之前應先初始化。接受參數為實作了heap.Interface接口的對象。
Fix
func Fix(h Interface, i int)
在修改第i個元素後,調用本函數修複堆,比删除第i個元素後插入新元素更有效率。
Push&Pop
Push和Pop是一對标準堆操作,Push向堆添加一個新元素,Pop彈出并傳回堆頂元素,而在push和pop操作不會破壞堆的結構
Remove
删除堆中的第i個元素,并保持堆的限制性
執行個體練習1
代碼實作了一個小頂堆,堆中元素為長方形類,按照面積大小進行排序,使用slice作為基礎存儲。首先是類定義和接口實作,需要實作前面說到的五個接口。
package main
import (
"container/heap"
"fmt"
)
//定義一個正方形的結構體
type Rectangle struct {
width int
height int
}
//
func (rec *Rectangle) Area() int {
return rec.width * rec.width
}
// 定義一個堆結構體
type RectHeap []Rectangle
// 實作heap.Interface接口
func (rech RectHeap) Len() int {
return len(rech)
}
// 實作sort.Iterface
func (rech RectHeap) Swap(i, j int) {
rech[i], rech[j] = rech[j], rech[i]
}
func (rech RectHeap) Less(i, j int) bool {
return rech[i].Area() < rech[j].Area()
}
// 實作heap.Interface接口定義的額外方法
func (rech *RectHeap) Push(h interface{}) {
*rech = append(*rech, h.(Rectangle))
}
func (rech *RectHeap) Pop() (x interface{}) {
n := len(*rech)
x = (*rech)[n-1] // 傳回删除的元素
*rech = (*rech)[:n-1] // [n:m]不包括下标為m的元素
return x
}
func main() {
hp := &RectHeap{}
for i := 2; i < 6; i++ {
*hp = append(*hp, Rectangle{i, i})
}
fmt.Println("原始slice: ", hp)
// 堆操作
heap.Init(hp)
heap.Push(hp, Rectangle{100, 10})
fmt.Println("top元素:", (*hp)[0])
fmt.Println("删除并傳回最後一個:", heap.Pop(hp)) // 最後 一個元素
fmt.Println("最終slice: ", hp)
}
完成這個練習如果還有疑問?
那麼我們先來看看container/heap的源碼:
截取其中一部分:
// Push pushes the element x onto the heap. The complexity is
// O(log(n)) where n = h.Len().
//
func Push(h Interface, x interface{}) {
h.Push(x)
up(h, h.Len()-1)
}
// Pop removes the minimum element (according to Less) from the heap
// and returns it. The complexity is O(log(n)) where n = h.Len().
// It is equivalent to Remove(h, 0).
//
func Pop(h Interface) interface{} {
n := h.Len() - 1
h.Swap(0, n)
down(h, 0, n)
return h.Pop()
}
在調用heap.Push()後, 程式會再次調用堆對象的Push()方法進行操作, Pop亦是如此, 不過Pop做了swap(0, n) 。
執行個體練習2 - 包含int的最小堆
// This example demonstrates an integer heap built using the heap interface.
package heap_test
import (
"container/heap"
"fmt"
)
// An IntHeap is a min-heap of ints.
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
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) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
// This example inserts several ints into an IntHeap, checks the minimum,
// and removes them in order of priority.
func Example_intHeap() {
h := &IntHeap{2, 1, 5}
heap.Init(h)
heap.Push(h, 3)
fmt.Printf("minimum: %d\n", (*h)[0])
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
// Output:
// minimum: 1
// 1 2 3 5
}
執行個體練習3 - 用heap建立一個優先級隊列
// This example demonstrates a priority queue built using the heap interface.
package heap_test
import (
"container/heap"
"fmt"
)
// 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
}
有疑問加站長微信聯系(非本文作者)