Golang標準庫深入 - 堆(container/heap)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

 

 

概述

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

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.