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()
}
相关推荐
夜月yeyue2 小时前
ARM内核与寄存器
arm开发·stm32·单片机·嵌入式硬件·mcu·链表
不知名美食探索家4 小时前
【11】Redis快速安装与Golang实战指南
redis·golang·bootstrap
普通网友4 小时前
内置AI与浏览器的开源终端Wave Terminal安装与远程连接内网服务器教程
开发语言·后端·golang
行思理5 小时前
go语言应该如何学习
开发语言·学习·golang
returnShitBoy6 小时前
Go语言中的垃圾回收是如何工作的?
java·jvm·golang
しかし1181146 小时前
C语言队列的实现
c语言·开发语言·数据结构·数据库·经验分享·链表
普通网友6 小时前
如何在CentOS部署青龙面板并实现无公网IP远程访问本地面板
开发语言·后端·golang
LuckyLay11 小时前
LeetCode算法题(Go语言实现)_38
算法·leetcode·golang
二狗哈15 小时前
go游戏后端开发31:麻将游戏的碰牌与胡牌逻辑
服务器·游戏·golang
_x_w16 小时前
【12】数据结构之基于线性表的排序算法
开发语言·数据结构·笔记·python·算法·链表·排序算法