|
| 1 | +--- |
| 2 | +title: "Golang: container/heap" |
| 3 | +subtitle: "https://leetcode.com/problems/kth-largest-element-in-a-stream/" |
| 4 | +date: 2023-01-22T21:20:00+08:00 |
| 5 | +lastmod: 2023-01-22T21:20:00+08:00 |
| 6 | +draft: false |
| 7 | +author: "Kimi.Tsai" |
| 8 | +authorLink: "https://kimi0230.github.io/" |
| 9 | +description: "0703.Kth-Largest-Element-in-a-Stream" |
| 10 | +license: "" |
| 11 | +images: [] |
| 12 | + |
| 13 | +tags: [Go, container/heap] |
| 14 | +categories: [Structure] |
| 15 | + |
| 16 | +featuredImage: "" |
| 17 | +featuredImagePreview: "" |
| 18 | + |
| 19 | +hiddenFromHomePage: false |
| 20 | +hiddenFromSearch: false |
| 21 | +twemoji: false |
| 22 | +lightgallery: true |
| 23 | +ruby: true |
| 24 | +fraction: true |
| 25 | +fontawesome: true |
| 26 | +linkToMarkdown: false |
| 27 | +rssFullText: false |
| 28 | + |
| 29 | +toc: |
| 30 | + enable: true |
| 31 | + auto: true |
| 32 | +code: |
| 33 | + copy: true |
| 34 | + maxShownLines: 200 |
| 35 | +math: |
| 36 | + enable: false |
| 37 | + # ... |
| 38 | +mapbox: |
| 39 | + # ... |
| 40 | +share: |
| 41 | + enable: true |
| 42 | + # ... |
| 43 | +comment: |
| 44 | + enable: true |
| 45 | + # ... |
| 46 | +library: |
| 47 | + css: |
| 48 | + # someCSS = "some.css" |
| 49 | + # located in "assets/" |
| 50 | + # Or |
| 51 | + # someCSS = "https://cdn.example.com/some.css" |
| 52 | + js: |
| 53 | + # someJS = "some.js" |
| 54 | + # located in "assets/" |
| 55 | + # Or |
| 56 | + # someJS = "https://cdn.example.com/some.js" |
| 57 | +seo: |
| 58 | + images: [] |
| 59 | + # ... |
| 60 | +--- |
| 61 | + |
| 62 | +# Golang : container/heap |
| 63 | + |
| 64 | +## Heap是什麼 |
| 65 | +Wiki: https://zh.wikipedia.org/wiki/%E5%A0%86%E7%A9%8D |
| 66 | + |
| 67 | +堆積(Heap)是電腦科學中的一種特別的**完全二元樹**。 |
| 68 | +若是滿足以下特性,即可稱為堆積:「給定堆積中任意節點P和C,若P是C的母節點,那麼P的值會小於等於(或大於等於)C的值」。 |
| 69 | +若母節點的值恆小於等於子節點的值,此堆積稱為最小堆積(min heap); |
| 70 | +反之,若母節點的值恆大於等於子節點的值,此堆積稱為最大堆積(max heap)。 |
| 71 | +在堆積中最頂端的那一個節點,稱作根節點(root node),根節點本身沒有母節點(parent node)。 |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +## container/heap 提供的方法 |
| 76 | +heap包為實現了 `heap.Interface` 的類型提供了堆方法:Init/Push/Pop/Remove/Fix。 `container/heap` 為最小堆, |
| 77 | +即每個節點的值都小於它的子樹的所有元素的值(A heap is a tree with the property that each node is the minimum-valued node in its subtree)。 |
| 78 | + |
| 79 | +**heap:** |
| 80 | +```go |
| 81 | +package heap |
| 82 | +// ... |
| 83 | +// Note that Push and Pop in this interface are for package heap's |
| 84 | +// implementation to call. To add and remove things from the heap, |
| 85 | +// use heap.Push and heap.Pop. |
| 86 | +type Interface interface { |
| 87 | + sort.Interface |
| 88 | + Push(x any) // add x as element Len() |
| 89 | + Pop() any // remove and return element Len() - 1. |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +**sort:** |
| 94 | +```go |
| 95 | +package sort |
| 96 | + |
| 97 | +// An implementation of Interface can be sorted by the routines in this package. |
| 98 | +// The methods refer to elements of the underlying collection by integer index. |
| 99 | +type Interface interface { |
| 100 | + // Len is the number of elements in the collection. |
| 101 | + Len() int |
| 102 | + |
| 103 | + // Less reports whether the element with index i |
| 104 | + // must sort before the element with index j. |
| 105 | + // |
| 106 | + // If both Less(i, j) and Less(j, i) are false, |
| 107 | + // then the elements at index i and j are considered equal. |
| 108 | + // Sort may place equal elements in any order in the final result, |
| 109 | + // while Stable preserves the original input order of equal elements. |
| 110 | + // |
| 111 | + // Less must describe a transitive ordering: |
| 112 | + // - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well. |
| 113 | + // - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well. |
| 114 | + // |
| 115 | + // Note that floating-point comparison (the < operator on float32 or float64 values) |
| 116 | + // is not a transitive ordering when not-a-number (NaN) values are involved. |
| 117 | + // See Float64Slice.Less for a correct implementation for floating-point values. |
| 118 | + Less(i, j int) bool |
| 119 | + |
| 120 | + // Swap swaps the elements with indexes i and j. |
| 121 | + Swap(i, j int) |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +由於 heap.Interface 包含了 sort.Interface ,所以,目標類型需要包含如下方法:`Len`/`Less`/`Swap` ,`Push`/`Pop。` |
| 126 | + |
| 127 | +## container/heap 可用在哪 |
| 128 | + |
| 129 | +container/heap包可以用來構造優先順序佇列。 |
| 130 | + |
| 131 | +https://go.dev/play/p/77zrF3PurO4 |
| 132 | +```go |
| 133 | +// This example demonstrates a priority queue built using the heap interface. |
| 134 | +package main |
| 135 | + |
| 136 | +import ( |
| 137 | + "container/heap" |
| 138 | + "fmt" |
| 139 | +) |
| 140 | + |
| 141 | +// An Item is something we manage in a priority queue. |
| 142 | +type Item struct { |
| 143 | + value string // The value of the item; arbitrary. |
| 144 | + priority int // The priority of the item in the queue. |
| 145 | + // The index is needed by update and is maintained by the heap.Interface methods. |
| 146 | + index int // The index of the item in the heap. |
| 147 | +} |
| 148 | + |
| 149 | +// A PriorityQueue implements heap.Interface and holds Items. |
| 150 | +type PriorityQueue []*Item |
| 151 | + |
| 152 | +func (pq PriorityQueue) Len() int { return len(pq) } |
| 153 | + |
| 154 | +func (pq PriorityQueue) Less(i, j int) bool { |
| 155 | + // We want Pop to give us the highest, not lowest, priority so we use greater than here. |
| 156 | + return pq[i].priority > pq[j].priority |
| 157 | +} |
| 158 | + |
| 159 | +func (pq PriorityQueue) Swap(i, j int) { |
| 160 | + pq[i], pq[j] = pq[j], pq[i] |
| 161 | + pq[i].index = i |
| 162 | + pq[j].index = j |
| 163 | +} |
| 164 | + |
| 165 | +func (pq *PriorityQueue) Push(x interface{}) { |
| 166 | + n := len(*pq) |
| 167 | + item := x.(*Item) |
| 168 | + item.index = n |
| 169 | + *pq = append(*pq, item) |
| 170 | +} |
| 171 | + |
| 172 | +func (pq *PriorityQueue) Pop() interface{} { |
| 173 | + old := *pq |
| 174 | + n := len(old) |
| 175 | + item := old[n-1] |
| 176 | + item.index = -1 // for safety |
| 177 | + *pq = old[0 : n-1] |
| 178 | + return item |
| 179 | +} |
| 180 | + |
| 181 | +// update modifies the priority and value of an Item in the queue. |
| 182 | +func (pq *PriorityQueue) update(item *Item, value string, priority int) { |
| 183 | + item.value = value |
| 184 | + item.priority = priority |
| 185 | + heap.Fix(pq, item.index) |
| 186 | +} |
| 187 | +``` |
| 188 | + |
| 189 | +PriorityQueue 本質上是個 *Item 陣列,其Len/Less/Swap是比較常見的陣列用來sort需要定義的函數,而Push、Pop則是使用數位來插入、的方法。 PriorityQueue 還提供了update方法。 注意由於通常希望優先順序佇列Pop出來的是優先順序最高的元素,所以Less方法是反著寫的。 |
| 190 | + |
| 191 | +定義了以上方法以後, PriorityQueue 就具備了使用 container/heap 包的條件。 |
| 192 | + |
| 193 | + |
| 194 | +如下代碼,先從items map出發定義了一個pq陣列,長度為hash的size,並調用 heap.Init 初始化pq; |
| 195 | +之後向佇列中增加了一個優先順序為1的元素,並更新該元素的佇列; 最後從佇列中依此Pop,可見元素在Pop時是依照優先順序排序的。 |
| 196 | +```go |
| 197 | +// This example creates a PriorityQueue with some items, adds and manipulates an item, |
| 198 | +// and then removes the items in priority order. |
| 199 | +func main() { |
| 200 | + // Some items and their priorities. |
| 201 | + items := map[string]int{ |
| 202 | + "banana": 3, "apple": 2, "pear": 4, |
| 203 | + } |
| 204 | + |
| 205 | + // Create a priority queue, put the items in it, and |
| 206 | + // establish the priority queue (heap) invariants. |
| 207 | + pq := make(PriorityQueue, len(items)) |
| 208 | + i := 0 |
| 209 | + for value, priority := range items { |
| 210 | + pq[i] = &Item{ |
| 211 | + value: value, |
| 212 | + priority: priority, |
| 213 | + index: i, |
| 214 | + } |
| 215 | + i++ |
| 216 | + } |
| 217 | + heap.Init(&pq) |
| 218 | + |
| 219 | + // Insert a new item and then modify its priority. |
| 220 | + item := &Item{ |
| 221 | + value: "orange", |
| 222 | + priority: 1, |
| 223 | + } |
| 224 | + heap.Push(&pq, item) |
| 225 | + pq.update(item, item.value, 5) |
| 226 | + |
| 227 | + // Take the items out; they arrive in decreasing priority order. |
| 228 | + for pq.Len() > 0 { |
| 229 | + item := heap.Pop(&pq).(*Item) |
| 230 | + fmt.Printf("%.2d:%s index:%d \n", item.priority, item.value, item.index) |
| 231 | + } |
| 232 | +} |
| 233 | +// Output: |
| 234 | +// 05:orange index:-1 |
| 235 | +// 04:pear index:-1 |
| 236 | +// 03:banana index:-1 |
| 237 | +// 02:apple index:-1 |
| 238 | + |
| 239 | +``` |
| 240 | + |
| 241 | +## Reference |
| 242 | +* [Golang: 详解container/heap](https://ieevee.com/tech/2018/01/29/go-heap.html#3-containerheap%E5%8F%AF%E4%BB%A5%E7%94%A8%E6%9D%A5%E5%81%9A%E4%BB%80%E4%B9%88) |
0 commit comments