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()
}
相关推荐
树在风中摇曳2 小时前
链表五大经典面试题详解:双指针与基础操作实战
数据结构·链表
ChineHe4 小时前
Golang并发编程篇002_Go并发基础
开发语言·后端·golang
麻辣兔变形记18 小时前
基于 Go‑Zero 的用户 CRUD Demo:如何一步步从 MySQL + sqlx 演进为 PostgreSQL + GORM + 微服务架构
mysql·微服务·postgresql·架构·golang
无限进步_19 小时前
C语言单向链表实现详解:从基础操作到完整测试
c语言·开发语言·数据结构·c++·算法·链表·visual studio
i***279521 小时前
【golang学习之旅】使用VScode安装配置Go开发环境
vscode·学习·golang
无敌最俊朗@1 天前
力扣hot100 - 合并两个有序链表21
算法·leetcode·链表
le serein —f2 天前
用go实现-反转链表
leetcode·链表·golang
席万里2 天前
关于Go的init函数执行顺序#黑魔法
开发语言·网络·golang
hweiyu002 天前
数据结构:链表
数据结构·链表
喵了几个咪2 天前
Golang微服务框架kratos实现Socket.IO服务
开发语言·微服务·golang