1. 程式人生 > >Golang中heap包原始碼分析

Golang中heap包原始碼分析

heap的實現使用到了小根堆,下面先對堆做個簡單說明

1. 堆概念  

  堆是一種經過排序的完全二叉樹,其中任一非終端節點的資料值均不大於(或不小於)其左孩子和右孩子節點的值。

  最大堆和最小堆是二叉堆的兩種形式。

  最大堆:根結點的鍵值是所有堆結點鍵值中最大者。

  最小堆:根結點的鍵值是所有堆結點鍵值中最小者。

2. heap

 樹的最小元素在根部,為index 0.

 heap包對任意實現了heap介面的型別提供堆操作。

 heap是常用的實現優先佇列的方法。要建立一個優先佇列,實現一個具有使用(負的)優先順序作為比較的依據的Less方法的Heap介面,如此一來可用Push新增專案而用Pop取出佇列最高優先順序的專案。

 

heap需要實現的介面

// Any type that implements heap.Interface may be used as a
// min-heap with the following invariants (established after
// Init has been called or if the data is empty or sorted):
//
//	!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
//
// Note that Push and Pop in this interface are for package heap's
// implementation to call. To add and remove things from the heap,
// use heap.Push and heap.Pop.
type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}
// 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 Fix(h Interface, i int)            //在修改第i個元素後,呼叫本函式修復堆,比刪除第i個元素後插入新元素更有效率。複雜度O(log(n)),其中n等於h.Len()。
func Init(h Interface)               //初始化一個堆。一個堆在使用任何堆操作之前應先初始化。Init函式對於堆的約束性是冪等的(多次執行無意義),並可能在任何時候堆的約束性被破壞時被呼叫。本函式複雜度為O(n),其中n等於h.Len()。
func Pop(h Interface) interface{}         //刪除並返回堆h中的最小元素(不影響約束性)。複雜度O(log(n)),其中n等於h.Len()。該函式等價於Remove(h, 0)。
func Push(h Interface, x interface{})      //向堆h中插入元素x,並保持堆的約束性。複雜度O(log(n)),其中n等於h.Len()。
func Remove(h Interface, i int) interface{}  //刪除堆中的第i個元素,並保持堆的約束性。複雜度O(log(n)),其中n等於h.Len()。

 

4.示例

(1) 儲存int型別元素的heap實現


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{}) {
	if v,ok := x.(int); ok {
		*h = append(*h,v)
	}
}

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{5,7,9,2}
	heap.Init(h)
	heap.Push(h,3)
	fmt.Println((*h)[0])
	for h.Len() > 0 {
		fmt.Printf("%d ", heap.Pop(h))
	}
}

執行結果:(取出的元素是從小到大)
2
2 3 5 7 9 

(2) 實現heap的優先佇列(以下程式碼摘自nsq)


type Item struct {
	Value    interface{}
	Priority int64   //優先順序
	Index    int
}

// this is a priority queue as implemented by a min heap
// ie. the 0th element is the *lowest* value
//PriorityQueue實現了heap介面
type PriorityQueue []*Item

func New(capacity int) PriorityQueue {
	return make(PriorityQueue, 0, capacity)
}

/*
Len(),Less()和Swap用於按Priority排序
*/
func (pq PriorityQueue) Len() int {
	return len(pq)
}

func (pq PriorityQueue) Less(i, j int) bool {
	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)
	c := cap(*pq)
	if n+1 > c { //擴容
		npq := make(PriorityQueue, n, c*2)
		copy(npq, *pq)
		*pq = npq
	}

	*pq = (*pq)[0 : n+1]
	item := x.(*Item)
	item.Index = n
	(*pq)[n] = item
}

func (pq *PriorityQueue) Pop() interface{} {
	n := len(*pq)
	c := cap(*pq)
	if n < (c/2) && c > 25 {//縮容
		npq := make(PriorityQueue, n, c/2)
		copy(npq, *pq)
		*pq = npq
	}
	item := (*pq)[n-1]
	item.Index = -1
	*pq = (*pq)[0 : n-1]
	return item
}


func main() {


	item1 := &Item{
		Value  : "aaaa",
		Priority : 5,
	}

	item2 := &Item{
		Value  : "bbbb",
		Priority : 2,
	}

	item3 := &Item{
		Value  : "cccc",
		Priority : 10,
	}

	item4 := &Item{
		Value  : "dddd",
		Priority : 1,
	}

	pq := New(100)
	heap.Push(&pq,item1)
	heap.Push(&pq,item2)
	heap.Push(&pq,item3)
	heap.Push(&pq,item4)


	for pq.Len() > 0 {
		fmt.Println(heap.Pop(&pq))
	}

}


執行結果:
&{dddd 1 -1}
&{bbbb 2 -1}
&{aaaa 5 -1}
&{cccc 10 -1}

 

參考地址:https://www.cnblogs.com/davygeek/p/5916230.html