测试环境

go version go1.11.2 windows/amd64

概述

heap, 即堆, 是一种用数组实现的完全二叉树.
堆有大根堆和小根堆, 分别是说: 对应的二叉树的树根结点的键值是所有堆节点键值中最大/小者。
为了方便叙述, 以小根堆为例说下概念.
堆常见的实现方法: 在数组(arr)中存储若干个元素, 如果

ji
interface{}$GOROOT/src/container/heap/heap.go

例子演示

$GOROOT/src/container/heap/example_intheap_test.go

MonthHeap

MonthHeap是这样的, 它里边存放月份的英文简写, 序号较前的月份应该在堆顶.
完整源代码在这里;

package month_heap

import (
	"strings"
)

var monthMap map[string]int = make(map[string]int)

func init() {
	months := strings.Split("Jan, Feb, Mar, Apr, May, June, July, Aug, Sep, Oct, Nov, Dec", ",")
	for i, v := range months { // 将月份简写与序号对应
		monthMap[strings.TrimSpace(v)] = i + 1
	}
}

type MonthHeap []string

// 实现heap.Interface需要的5个方法
func (m MonthHeap) Len() int {
	return len(m)
}

// 注意这里: 比较的是哪个月份在前
func (m MonthHeap) Less(i, j int) bool {
	return monthMap[m[i]] < monthMap[m[j]]
}

func (m MonthHeap) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

// Push和Pop都要用指针接收者, 因为要在函数内修改slice
func (m *MonthHeap) Push(x interface{}) {
	*m = append(*m, x.(string))
}

func (m *MonthHeap) Pop() interface{} {
	old := *m
	n := len(old)
	x := old[n-1]
	*m = old[0 : n-1]
	return x
}

测试如下:

func MonthHeapTest() {
	h := &month_heap.MonthHeap{"Jan", "Feb", "Mar"}
	heap.Init(h)
	h.Push("May")
	h.Push("Apr")
	// first month: Jan
	fmt.Println("first month:", (*h)[0])

	// 输出: Jan Feb Mar Apr May
	for h.Len() > 0 {
		fmt.Printf("%s\t", heap.Pop(h)) // 注意不是h.Pop()
	}
	fmt.Println()
}

优先级队列priority_queue

完整代码在这里

package priority_queue

import "container/heap"

type Item struct {
	Value    string // 值
	Priority int    // 优先级
	Index    int    // 元素在堆中的索引
}

type PriorityQueue []*Item

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{}) {
	item := x.(*Item)
	n := len(*pq)
	item.Index = n
	*pq = append(*pq, item)
}

func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	x := old[n-1]

	// 为了安全起见? 应该是防止直接用这个值插入或者别的,
	// 将index设置为一个越界值, 可以防止误操作
	x.Index = -1
	*pq = old[0 : n-1]
	return x
}

// 更新queue中一个Item的Priority和value, 将将Item调整到queue中适当的位置(满足堆的特征)
// 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

	// Fix操作比 先Remove再Push要快
	heap.Fix(pq, item.Index)
}

测试代码:

func PriorityQueueTest() {
	items := map[string]int{
		"AA": 5,
		"BB": 8,
		"CC": 3,
	}
	pq := make(priority_queue.PriorityQueue, len(items))
	i := 0

	for value, priority := range items {
		pq[i] = &priority_queue.Item{
			Value:    value,
			Priority: priority,
			Index:    i,
		}
		i++
	}
	heap.Init(&pq)

	item := priority_queue.Item{
		Value:    "DD",
		Priority: 1,
	}
	pq.Push(&item)
	pq.Update(&item, "EE", 99) // 修改名字并调整优先级

	// EE:99 BB:8 AA:5 CC:3
	for pq.Len() > 0 {
		x := heap.Pop(&pq).(*priority_queue.Item)
		fmt.Printf("%s:%d\t", x.Value, x.Priority)
	}
	fmt.Println()
}

欢迎补充指正!