這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
概述
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.InterfacePush(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 mainimport ("container/heap""fmt")//定義一個正方形的結構體type Rectangle struct {width intheight 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.Iterfacefunc (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() - 1h.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_testimport ( "container/heap" "fmt")// An IntHeap is a min-heap of ints.type IntHeap []intfunc (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_testimport ( "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 []*Itemfunc (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}
最後,如果你有疑問,為什麼要使用heap包來操作, 請轉站: https://studygolang.com/articles/3719