使用heap库之前,首先要实现其定义的五个函数,分别是:

type Interface interface {
    // 堆的大小,通常返回的是切片的长度
    Len() int
    // 比较器,函数返回值为true时表示切片中i索引的元素比较“小”
	Less(i, j int) bool
    // 交换i和j两个坐标的元素
	Swap(i, j int)
    // 将元素x加入到堆中
	Push(x any)
    // 弹出堆顶元素
	Pop() any
}

一个大根堆的实现示例:

type bHp struct{ sort.IntSlice }

func (hp bHp) Less(i, j int) bool { return hp.IntSlice[i] > hp.IntSlice[j] }

func (hp bHp) Peek() interface{} {
	a := hp.IntSlice
	return a[0]
}

func (hp *bHp) Push(x interface{}) {
	hp.IntSlice = append(hp.IntSlice, x.(int))
}

func (hp *bHp) Pop() interface{} {
	a := hp.IntSlice
	x := a[len(a)-1]
	hp.IntSlice = a[:len(a)-1]
	return x
}

注意事项:

sort.IntSliceLen()Less()Swap()Less()iji

对自定义的堆进行操作

使用heap库已经定义好的函数,将实例化好的自定义堆的指针传进去即可。

// 实例化自定义堆
pq := &bHp{}
// 插入值
heap.Push(pq, 8)
heap.Push(pq, 9)
// 获取堆顶元素但不删除
fmt.Printf("当前堆顶元素为:%d\n", pq.Peek())
heap.Push(pq, 7)
heap.Push(pq, 10)
heap.Push(pq, 5)
heap.Push(pq, 7)
// 获取堆的大小
fmt.Printf("当前堆大小为:%d\n", pq.Len())
// 将所有的元素弹出
for pq.Len() > 0 {
    fmt.Println(heap.Pop(pq))
}

为什么自定义的bHp结构没有实现Len函数却能直接调用?

这个问题在上文已经提到过,bHp结构体直接使用sort.IntSlice类型作为成员,就拥有了sort.IntSlice中实现的所有函数,可以直接调用。

Pop函数的实现中为什么取的是切片中最后一个值?

首先我们要知道,在heap库内部维护堆这个结构时,堆顶(即堆中“最小”的元素)是处于切片的第一个元素

bhp.Pop()heap.Pop(bhp)heap.Pop()
// Pop removes and returns the minimum element (according to Less) from the heap.
// The complexity is O(log n) where n = h.Len().
// Pop is equivalent to Remove(h, 0).
func Pop(h Interface) any {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}

可以看到,在Pop函数源码中调用我们自定义的Pop函数之前,还进行了以下操作:

  • 将底层切片的第一个元素(堆顶)与最后一个元素进行交换
  • 对切片的第一个元素做“heapify”(元素下沉)操作

heapify操作指的是将指定的元素不断与其子结点进行比较,如果它的值比子结点要“大”,则两数交换,直到指定元素到达正确的位置为止。

所以,在调用我们自己实现的Pop函数之前,原堆顶元素就已经被交换到底层切片的末尾了,这时我们取切片的最后一个元素就是堆顶。

对Peek操作的一些解释:

  1. heap库并没有定义一个Peek函数,所以这里肯定是不能使用heap.Peek()这种写法的
  2. 自定义的Peek函数,直接返回底层切片的第一个元素值。这个其实上一问理解了的话就很清楚了,因为堆顶元素本来就处在底层切片的第一位

实现接口函数的时候,结构体参数定义为什么有的用指针有的不用?

在Golang中,如果将结构体直接作为函数参数进行传递的话,它是属于值传递的,也就是说程序会将原本的结构体拷贝一份再传到函数里面。

如果用的是值传递的方式,而且在函数内部中对这个结构体进行修改了,外部是不可见的,因为这根本就是两个不同的实例。

PushPopLessPeek

我的 个人博客 上线啦,欢迎到访~