Go中的堆结构+大根堆小根堆的接口实现方法

1.Heap接口说明

在Go中,堆结构的实现依赖于使用者对于Heap接口进行实现,更加灵活,但也需要额外的工作量。

import (
	"container/heap"
)

在Heap.go中定义了接口如下:

type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

可以发现,要实现这个接口需要我们实现2个方法和一个sort接口,我们首先来看这两个方法。

2.Push和Pop方法实现

我们首先来对Heap中的两个方法Push和Pop进行实现:

Push的官方注释add x as element Len()只是让我们把数字放在heap的末尾。

手动实现:

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

但是使用过程中,调用Push操作时,会先调用Heap.go中的Push(h Interface,x interface{})

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

由这个函数再调用我们实现的Push(x interface{})将数放入heap末尾,使用原生写好的up函数将其向上放置合适的位置。

func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // parent
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		j = i
	}
}

Pop函数官方注释remove and return element Len() - 1.需要我们移出一个元素并将数组长度-1,也就是说将数组的末元素移出。

手动实现:

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

我们只是移出了末尾元素,如何保证末尾元素就是最大/最小值呢?

和Push一样,Pop也会首先调用官方的Heap.go里的函数,Pop(h Interface) interface{}

// 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) interface{} {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}

         可以看到,就如同手动删除大小根堆的元素一样,我们会将堆的首末元素呼唤,然后将末元素在首位置使用down函数向下调整至合适的位置然后再删除末尾的原首元素,其中的down函数也是原生实现好的。

func down(h Interface, i0, n int) bool {
	i := i0
	for {
		j1 := 2*i + 1
		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
			break
		}
		j := j1 // left child
		if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
			j = j2 // = 2*i + 2  // right child
		}
		if !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		i = j
	}
	return i > i0
}

3.sort接口实现

上文提到我们还要实现sort接口。

// Package sort provides primitives for sorting slices and user-defined
// collections.
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)
}

我们还需要对sort中的三个方法进行实现。

func (h IntHeap) Len() int { return len(h) }
// Less 为了实现大根堆,Less在大于时返回小于
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] }

至此我们就实现了一个大根堆的构建,小根堆只需要改动sort方法即可。

使用中,需要对堆进行初始化:

type IntHeap []int

 同时,对堆进行大根堆的初始化

func Init(h Interface) {
	// heapify
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}

 

posted @ 2021-10-30 15:48  梦想是能睡八小时的猪  阅读(960)  评论(0)    收藏  举报