golang 使用双向链表作为container/heap的载体

MyHeap:container/heap的数据载体,需要实现以下方法:

Len:堆中数据个数

Less:第i个元素 是否必 第j个元素 值小

Swap:交换第i个元素和 第j个元素

Push:向堆中追加元素

Pop:从堆中取出元素

下面是使用双向链路作为数据载体的最小堆实现方式:

Go 复制代码
package main

import (
	"container/heap"
	"fmt"
)

type HeapItem struct {
	Value int
	Prev  *HeapItem
	Next  *HeapItem
}

type MyHeap struct {
	Head   *HeapItem
	Tail   *HeapItem
	Length int
}

func (h *MyHeap) Len() int {
	return h.Length
}

func (h *MyHeap) Less(i, j int) bool {
	return h.Find(i).Value < h.Find(j).Value
}

func (h *MyHeap) Swap(i, j int) {
	nodeI, nodeJ := h.Find(i), h.Find(j)
	isNear := h.IsNear(nodeI, nodeJ)

	// 记录I的前和后
	nodeIPrev, nodeINext := nodeI.Prev, nodeI.Next

	// 记录J的前和后
	nodeJPrev, nodeJNext := nodeJ.Prev, nodeJ.Next

	// 把J放到I的位置
	nodeIPrev.Next = nodeJ
	nodeJ.Prev = nodeIPrev
	nodeJ.Next = nodeINext // near, 对于相邻元素, 这样操作有问题, 下面会重新赋值
	nodeINext.Prev = nodeJ // near, 对于相邻元素, 这样操作有问题, 下面会重新赋值

	// 把I放到J的位置
	nodeJPrev.Next = nodeI // near, 对于相邻元素, 这样操作有问题, 下面会重新赋值
	nodeI.Prev = nodeJPrev // near, 对于相邻元素, 这样操作有问题, 下面会重新赋值
	nodeI.Next = nodeJNext
	nodeJNext.Prev = nodeI

	// 对于相邻元素,重新赋值
	if isNear {
		nodeJ.Next = nodeI
		nodeINext.Prev = nodeIPrev

		nodeJPrev.Next = nodeJNext
		nodeI.Prev = nodeJ
	}
}

func (h *MyHeap) Push(v interface{}) {
	newItem := v.(*HeapItem)

	temp := h.Tail.Prev
	temp.Next = newItem
	newItem.Prev = temp
	newItem.Next = h.Tail
	h.Tail.Prev = newItem

	h.Length++
	return
}

func (h *MyHeap) Pop() interface{} {
	realTailNode := h.Tail.Prev

	realTailNode.Prev.Next = realTailNode.Next
	realTailNode.Next.Prev = realTailNode.Prev

	h.Length--

	return realTailNode
}

func (h *MyHeap) IsNear(nodeI, nodeJ *HeapItem) bool {
	if nodeI.Next == nodeJ {
		return true
	}
	return false
}

func (h *MyHeap) Find(i int) *HeapItem {
	nodeI := h.Head
	for k := 0; k <= i; k++ {
		nodeI = nodeI.Next
	}
	return nodeI
}

func (h *MyHeap) Show() {
	forward := ""
	backward := ""
	i := 0
	for curr := h.Head; curr != nil && i < 10; curr = curr.Next {
		forward += fmt.Sprintf("'%d'->", curr.Value)
		i++
	}
	j := 0
	for curr := h.Tail; curr != nil && j < 10; curr = curr.Prev {
		backward = fmt.Sprintf("'%d'<-", curr.Value) + backward
		j++
	}
	fmt.Printf("forward=%s, backward=%s\n", forward, backward)
}

func InitHeap() *MyHeap {
	head := &HeapItem{Value: -1}
	tail := &HeapItem{Value: -2}
	head.Next = tail
	tail.Prev = head
	return &MyHeap{
		Head:   head,
		Tail:   tail,
		Length: 0,
	}
}

func main() {
	myHeap := InitHeap()
	heap.Init(myHeap)
	heap.Push(myHeap, &HeapItem{Value: 10})
	heap.Push(myHeap, &HeapItem{Value: 1000})
	heap.Push(myHeap, &HeapItem{Value: 5})
	heap.Push(myHeap, &HeapItem{Value: 1})
	heap.Push(myHeap, &HeapItem{Value: 7})

	myHeap.Show()

	for myHeap.Len() > 0 {
		item := heap.Pop(myHeap).(*HeapItem)
		fmt.Printf("%d ", item.Value)
	}
	fmt.Println()
}
相关推荐
熬了夜的程序员3 小时前
【华为机试】HJ61 放苹果
算法·华为·面试·golang
亚洲第一中锋_哈达迪4 小时前
详解缓存淘汰策略:LRU
后端·缓存·golang
卜锦元19 小时前
Go中使用wire进行统一依赖注入管理
开发语言·后端·golang
mit6.8241 天前
论容器化 | 分析Go和Rust做医疗的后端服务
docker·golang·rust
ykuaile_h81 天前
Go 编译报错排查:vendor/golang.org/x/crypto/cryptobyte/asn1 no Go source files
后端·golang
科大饭桶1 天前
数据结构自学Day5--链表知识总结
数据结构·算法·leetcode·链表·c
YuTaoShao1 天前
【LeetCode 热题 100】24. 两两交换链表中的节点——(解法一)迭代+哨兵
java·算法·leetcode·链表
凤年徐1 天前
【数据结构与算法】203.移除链表元素(LeetCode)图文详解
c语言·开发语言·数据结构·算法·leetcode·链表·刷题
Nejosi_念旧2 天前
解读 Go 中的 constraints包
后端·golang·go
风无雨2 天前
GO 启动 简单服务
开发语言·后端·golang