天天看點

php 最小堆,Golang标準庫深入 - 堆(container/heap)

概述

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.Interface

Push(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 main

import (

"container/heap"

"fmt"

)

//定義一個正方形的結構體

type Rectangle struct {

width int

height 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.Iterface

func (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() - 1

h.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_test

import (

"container/heap"

"fmt"

)

// An IntHeap is a min-heap of ints.

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 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_test

import (

"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 []*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

}

有疑問加站長微信聯系(非本文作者)