Go标准库数据结构 Heap

107 阅读2分钟

堆(heap)是实际中比较常用的数据结构,Go标准库container/heap中实现该数据结构,只需根据实际情况进行接口实现即可。

堆的概念

特性:

  • 堆是完全二叉树
  • 堆中每个节点的值都**大于等于(或小于等于)**其子树中每个节点的值

分类:

  • 小顶堆:堆中每个节点的值都必须小于等于其子树中每个节点的值
  • 大顶堆:堆中每个节点的值都必须大于等于其子树中每个节点的值

Go的堆实现

基于Go的标准库container/heap,只需进行接口实现即可

需实现的接口为:

// The Interface type describes the requirements
// for a type using the routines in this package.
// Any type that implements it 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接口,所以也要实现里面的方法
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

其中sort.Interface接口方法为

package sort

// 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)
}

因此基于container/heap实现堆结构只需实现上述五个接口即可

代码实现

小顶堆实现代码如下,实现大小顶堆的区别在Less(i, j int) bool 接口

import (
	"container/heap"
	"fmt"
)

type IntHeap []int

func main() {
	h := &IntHeap{2, 1, 5, 6, 4, 3, 7, 9, 8, 0} // 创建slice
	heap.Init(h)                                // 将数组切片进行堆化
	fmt.Println(*h)                             // [0 1 3 6 2 5 7 9 8 4] 由Less方法可控制小顶堆
	fmt.Println(heap.Pop(h))                    // 调用pop 0 返回移除的顶部最小元素
	heap.Push(h, 6)                             // 调用push [1 2 3 6 4 5 7 9 8] 添加一个元素进入堆中进行堆化
	fmt.Println("new: ", *h)                    // [1 2 3 6 4 5 7 9 8 6]
	for len(*h) > 0 {                           // 持续推出顶部最小元素
		fmt.Printf("%d \n ", heap.Pop(h))
	}
}

func (h IntHeap) Len() int { return len(h) }

// 这里决定 大小顶堆 现在是小顶堆。大顶堆变成 h[i]>h[j] 即可。
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) Pop() interface{} {
	old := *h
	n := len(old)
	fmt.Println("old: ", old) // [1 2 3 6 4 5 7 9 8 0] 将顶小堆元素与最后一个元素交换位置,在进行堆排序的结果

	x := old[n-1]
	*h = old[0 : n-1]
	fmt.Println(*h) // [1 2 3 6 4 5 7 9 8]
	return x
}
func (h *IntHeap) Push(x interface{}) { // 绑定push方法,插入新元素
	*h = append(*h, x.(int))
}

对于Int类型的小顶堆,可通过匿名结构体嵌套来简化实现接口的个数

// 小顶堆
type hp struct{
  sort.IntSlice
}
func (h *hp) Push(v interface{}){
  h.IntSlice = append(h.IntSlice, v.(int))
}
func (h *hp) Pop() interface{}   { 
  a := h.IntSlice
  v := a[len(a)-1]
  h.IntSlice = a[:len(a)-1]
  return v 
}

reference