golang 優先級隊列


 

 

container/heap

本文是 Go 標准庫中 container/heap 包文檔的翻譯, 原文地址為: https://golang.org/pkg/container/heap/

概述

包 heap 為所有實現了 heap.Interface 的類型提供堆操作。 一個堆即是一棵樹, 這棵樹的每個節點的值都比它的子節點的值要小, 而整棵樹最小的值位於樹根(root), 也即是索引 0 的位置上。

堆是實現優先隊列的一種常見方法。 為了構建優先隊列, 用戶在實現堆接口時, 需要讓 Less() 方法返回逆序的結果, 這樣就可以在使用 Push 添加元素的同時, 通過 Pop 移除隊列中優先級最高的元素了。 具體的實現請看接下來展示的優先隊列例子。

示例:整數堆

// 這段代碼演示了如何使用堆接口構建一個整數堆。
package main import ( "container/heap" "fmt" ) // IntHeap 是一個由整數組成的最小堆。 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 和 Pop 使用 pointer receiver 作為參數, // 因為它們不僅會對切片的內容進行調整,還會修改切片的長度。 *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 } // 這個示例會將一些整數插入到堆里面, 接着檢查堆中的最小值, // 之后按順序從堆里面移除各個整數。 func main() { 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)) } } 

執行結果:

minimum: 1 1 2 3 5 

示例:優先隊列

// 這段代碼演示了如何使用堆接口構建一個優先隊列。
package main import ( "container/heap" "fmt" ) // Item 是優先隊列中包含的元素。 type Item struct { value string // 元素的值,可以是任意字符串。 priority int // 元素在隊列中的優先級。 // 元素的索引可以用於更新操作,它由 heap.Interface 定義的方法維護。 index int // 元素在堆中的索引。 } // 一個實現了 heap.Interface 接口的優先隊列,隊列中包含任意多個 Item 結構。 type PriorityQueue []*Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { // 我們希望 Pop 返回的是最大值而不是最小值, // 因此這里使用大於號進行對比。 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 // 為了安全性考慮而做的設置 *pq = old[0 : n-1] return item } // 更新函數會修改隊列中指定元素的優先級以及值。 func (pq *PriorityQueue) update(item *Item, value string, priority int) { item.value = value item.priority = priority heap.Fix(pq, item.index) } // 這個示例首先會創建一個優先隊列,並在隊列中包含一些元素 // 接着將一個新元素添加到隊列里面,並對其進行操作 // 最后按優先級有序地移除隊列中的各個元素。 func main() { // 一些元素以及它們的優先級。 items := map[string]int{ "banana": 3, "apple": 2, "pear": 4, } // 創建一個優先隊列,並將上述元素放入到隊列里面, // 然后對隊列進行初始化以滿足優先隊列(堆)的不變性。 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) // 插入一個新元素,然后修改它的優先級。 item := &Item{ value: "orange", priority: 1, } heap.Push(&pq, item) pq.update(item, item.value, 5) // 以降序形式取出並打印隊列中的所有元素。 for pq.Len() > 0 { item := heap.Pop(&pq).(*Item) fmt.Printf("%.2d:%s ", item.priority, item.value) } } 

執行結果:

05:orange 04:pear 03:banana 02:apple 

Fix 函數

func Fix(h Interface, i int) 

在索引 i 上的元素的值發生變化之后, 重新修復堆的有序性。 先修改索引 i 上的元素的值然后再執行 Fix , 跟先調用 Remove(h, i) 然后再使用 Push 操作將新值重新添加到堆里面的做法具有同等的效果, 但前者所需的計算量稍微要少一些。

Fix 函數的復雜度為 O(log(n)) , 其中 n 等於 h.Len() 。

Init 函數

func Init(h Interface) 

在執行任何堆操作之前, 必須對堆進行初始化。 Init 操作對於堆不變性(invariants)具有冪等性, 無論堆不變性是否有效, 它都可以被調用。

Init 函數的復雜度為 O(n) , 其中 n 等於 h.Len() 。

Pop 函數

func Pop(h Interface) interface{} 

Pop 函數根據 Less 的結果, 從堆中移除並返回具有最小值的元素, 等同於執行 Remove(h, 0) 。

Pop 函數的復雜度為 O(log(n)) , 其中 n 等於 h.Len() 。

Push 函數

func Push(h Interface, x interface{}) 

Push 函數將值為 x 的元素推入到堆里面, 該函數的復雜度為 O(log(n)) , 其中 n 等於 h.Len() 。

Remove 函數

func Remove(h Interface, i int) interface{} 

Remove 函數將移除堆中索引為 i 的元素, 該函數的復雜度為 O(log(n)) , 其中 n 等於 h.Len() 。

Interface 類型

任何實現了 heap.Interface 接口的類型, 都可以用作帶有以下不變性的最小堆, (換句話說, 這個堆在為空、已排序或者調用 Init 之后, 應該具有以下性質):

!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len() 

注意, 這個接口中的 Push 和 Pop 都是由 heap 包的實現負責調用的。 因此用戶在向堆添加元素又或者從堆中移除元素時, 需要使用 heap.Push 以及 heap.Pop :

type Interface interface { sort.Interface Push(x interface{}) // 將 x 添加為元素 Len() Pop() interface{} // 移除並返回元素 Len() - 1 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM