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()
}
相关推荐
Yeats_Liao1 小时前
Go Web 编程快速入门 13 - 部署与运维:Docker容器化、Kubernetes编排与CI/CD
运维·前端·后端·golang
Yeats_Liao1 小时前
Go Web 编程快速入门 14 - 性能优化与最佳实践:Go应用性能分析、内存管理、并发编程最佳实践
前端·后端·性能优化·golang
Dream it possible!2 小时前
LeetCode 面试经典 150_链表_旋转链表(64_61_C++_中等)
c++·leetcode·链表·面试
QX_hao3 小时前
【Go】--接口(interface)
开发语言·后端·golang
不爱笑的良田4 小时前
从零开始的云原生之旅(一):把 Go 应用塞进 Docker
docker·云原生·golang
数据知道5 小时前
Go语言设计模式:桥接模式详解
设计模式·golang·桥接模式
Wenhao.6 小时前
LeetCode LRU缓存
算法·leetcode·缓存·golang
数据知道10 小时前
Go语言设计模式:原型模式详解
设计模式·golang·原型模式
千码君201610 小时前
Go语言:常量计数器iota的意义
开发语言·后端·golang·状态码·const·iota·常量
ttghgfhhjxkl12 小时前
《macOS 配置 GO 语言后,如何切换不同 GO 版本?》
开发语言·macos·golang