go container/heap源码解析

本文介绍了如何实现堆数据结构及其操作,包括接口定义、堆化、插入与删除等方法,以IntHeap为例详细展示了小顶堆的实现过程和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


//heap的接口类:使用时候需要实现它的方法
type Interface interface {
	sort.Interface
	Push(x any) // add x as element Len()
	Pop() any   // remove and return element Len() - 1.
}

//shifDown()
//堆化(heapify):如果一个节点比它的子节点小(最大堆)或者大(最小堆),那么需要将它向下移动
func down(h Interface, i0, n int) bool {
	i := i0
	for {
		j1 := 2*i + 1          //获取i这个下标所在节点的left child
		if j1 >= n || j1 < 0 { //验证是否超过堆数组范围
			break
		}
		j := j1
		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
}

//shifUp()
//如果一个节点比它的父节点大(最大堆)或者小(最小堆),
//那么需要将它同父节点交换位置。这样是这个节点在数组的位置上升。
func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // floor((i - 1)/2):获取j的父节点
		//最小堆时,如果夫节点比子节点大,交换;
		//最大堆时,如果夫节点比子节点小,则交换
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		j = i
	}
}

//初始化时,从上往下堆化处理
func Init(h Interface) {
	// heapify
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}

//向尾部插入节点
func Push(h Interface, x any) {
	h.Push(x)        //先执行定义的方法
	up(h, h.Len()-1) //从下而上的排序堆
}

//返回并删除根节点数据
func Pop(h Interface) any {
	n := h.Len() - 1
	h.Swap(0, n)  //将根节点与最后一个节点交换
	down(h, 0, n) //堆化处理
	return h.Pop()
}

//删除指定节点
func Remove(h Interface, i int) any {
	n := h.Len() - 1
	if n != i {
		h.Swap(i, n)        //将删除的元素和最后一个元素交换
		if !down(h, i, n) { //堆化来修复,但是并不是唯一的情况,可能还需要up来向上修复
			up(h, i)
		}
	}
	return h.Pop()
}

示例


type IntHeap []int

func main() {
	h := &IntHeap{2, 1, 5, 6, 4, 3, 7, 9, 8, 0} // 创建slice
	heap.Init(h)                                // 将数组切片进行堆化
	dump.P(h)                                   // [0 1 3 6 2 5 7 9 8 4] 由Less方法可控制小顶堆
	dump.P(heap.Pop(h))                         // 调用pop 0 返回移除的顶部最小元素
	heap.Push(h, 6)                             // 调用push [1 2 3 6 4 5 7 9 8] 添加一个元素进入堆中进行堆化
	dump.P("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) }

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

	x := old[n-1]
	*h = old[0 : n-1]
	dump.P(h) // [1 2 3 6 4 5 7 9 8]
	return x
}

func (h *IntHeap) Push(x any) { // 绑定push方法,插入新元素
	*h = append(*h, x.(int))
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值