go版本:go1.12 windows/amd64
container目錄結(jié)構(gòu)如下:
├─heap
├─heap.go
├─heap_test.go
├─example_intheap_test.g
└─example_pq_test.go
├─list
├─list.go
├─list_test.go
├─example_test.go
└─ring
├─ring.go
├─ring_test.go
└─example_test.go
1. heap包介紹
??為所有繼承Interface的結(jié)構(gòu)體提供操作,heap是一個(gè)根節(jié)點(diǎn)為最小值的二叉樹(shù)(最小堆)
??heap包對(duì)任意實(shí)現(xiàn)了heap接口的類型提供堆操作。
??樹(shù)的最小元素在根部,為index 0.
??heap是常用的優(yōu)先隊(duì)列的方法,要?jiǎng)?chuàng)建一個(gè)優(yōu)先隊(duì)列,要實(shí)現(xiàn)一個(gè)具有負(fù)優(yōu)先級(jí)隊(duì)列,可以通過(guò)Less方法的Heap接口,如此一來(lái)可用Push添加item,而用Pop取出隊(duì)列最高優(yōu)先級(jí)的item。(后面解釋)
type Interface interface {
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}
// sort.Interface如下:
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
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)
}
??其中sort.Interface用于排序,heap.Interface中Push和Pop方法分別用于插入和取出元素的操作
??heap中的方法:
func Init(h Interface) // h初始化,按最小堆排序,時(shí)間復(fù)雜度O(n) ,n等于h.Len()
func Pop(h Interface) interface{} // 刪除并返回堆h中的最小元素(不影響約束性)。復(fù)雜度O(log(n)),n等于h.Len()。該函數(shù)等價(jià)于Remove(h, 0)。
func Push(h Interface, x interface{}) // 向堆h中插入元素x,并保持堆的約束性。復(fù)雜度O(log(n)),n等于h.Len()。
func Remove(h Interface, i int) interface{} // 刪除堆中的第i個(gè)元素,并保持堆的約束性。復(fù)雜度O(log(n)),n等于h.Len()。
func Fix(h Interface, i int) // 在修改第i個(gè)元素后,調(diào)用本函數(shù)修復(fù)堆,比刪除第i個(gè)元素后插入新元素更有效率。復(fù)雜度O(log(n)),n等于h.Len()。
2. heap(小頂堆) 構(gòu)造原理:
??小頂堆實(shí)際上是一個(gè)二叉樹(shù),滿足:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]規(guī)則,即根節(jié)點(diǎn)既小于或等于左子樹(shù)的關(guān)鍵字值,又小于或等于右子樹(shù)的關(guān)鍵字值。
??由此可知, n/2 - 1之前的節(jié)點(diǎn)都為父節(jié)點(diǎn),n/2-1之后的節(jié)點(diǎn)都為葉子節(jié)點(diǎn),我們可以從 n/2 - 1節(jié)點(diǎn)開(kāi)始,依次與其左葉子節(jié)點(diǎn)h[2(n/2-1)+1]和右葉子節(jié)點(diǎn)h[2(n/2-1)+2]比較,取三者最小值做為父節(jié)點(diǎn)
func Init(h Interface) {
// heapify
n := h.Len()
for i := n/2 - 1; i >= 0; i-- {
down(h, i, n)
}
}
func down(h Interface, i0, n int) bool {
i := i0
for {
j1 := 2*i + 1
if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
break
}
j := j1 // left child
if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
j = j2 // = 2*i + 2 // right child
}
if !h.Less(j, i) {
break
}
h.Swap(i, j)
i = j
}
return i > i0
}
// 將末尾元素移跟根節(jié)點(diǎn)元素交換,然后使用 down 方法來(lái)修復(fù)堆。最后Pop()取出末尾元素
func Pop(h Interface) interface{} {
n := h.Len() - 1
h.Swap(0, n)
down(h, 0, n)
return h.Pop()
}
// 每次添加新元素,若存在父節(jié)點(diǎn)則跟父節(jié)點(diǎn)比較,若小于父節(jié)點(diǎn),則交換
func Push(h Interface, x interface{}) {
h.Push(x)
up(h, h.Len()-1)
}
func up(h Interface, j int) {
for {
i := (j - 1) / 2 // parent
if i == j || !h.Less(j, i) {
break
}
h.Swap(i, j)
j = i
}
}
3.使用heap構(gòu)造優(yōu)先級(jí)隊(duì)列 (nsq源碼的優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)原理)
思路:通過(guò)重寫(xiě)Less()方法,取相反的優(yōu)先級(jí)比較,這樣Pop()函數(shù)每次取出的即是優(yōu)先級(jí)最高的Item.
// 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
}