Golang container包下heap(数据结构中的堆)的使用

mport "container/heap"

heap包提供了对任意类型(实现了heap.Interface接口)的堆操作。(最小)堆是具有“每个节点都是以其为根的子树中最小值”属性的树。

一、堆的基本概念

堆是一种经过排序的树形数据结构,每个节点都有一个值,通常我们所说的堆的数据结构是指二叉树。所以堆在数据结构中通常可以被看做是一棵树的数组对象。而且堆需要满足一下两个性质:
(1)堆中某个节点的值总是不大于或不小于其父节点的值;
(2)堆总是一棵完全二叉树。

Min-heap: 父节点的值小于或等于子节点的值;
Max-heap: 父节点的值大于或等于子节点的值;

堆的常用方法

  • 构建优先队列
  • 支持堆排序
  • 快速找出一个集合中的最小值(或者最大值)
  • 在朋友面前装逼
优先队列(priority queue)
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。
优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现。

本文主要讨论container包下heap的使用,因此原生的数据结构中的堆的基本操作在此不做讨论。heap已经封装了数据结构中堆基本操作。

二、示例

1、heap的简单使用

首先需要实现Interface对应的方法:

type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

其中 sort.Interface如下:

type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less reports whether the element with
	// index i should sort before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

示例代码如下:

package main
//堆的简单使用
import (
	"container/heap"
	"fmt"
)
type intHeap[]int  //定义堆对应的数据结构
//实现Interface对应的sort.Interface中的Len
func (h intHeap)Len()int  {
	return len(h)
}
//实现Interface对应的sort.Interface中的Less
func (h intHeap)Less(i, j int) bool {
	return h[i]<h[j]  //确定最大堆和最小堆
}
//实现Interface对应的sort.Interface中的Swap
func (h intHeap)Swap(i, j int)  {
	h[i],h[j]=h[j],h[i]
}
//实现Interface对应的Push
func (h *intHeap)Push(x interface{})  {  //要改变对应的数据,所以接受者为指针(地址传递)
  *h=append(*h,x.(int))
}
//实现Interface对应的Pop
func (h *intHeap)Pop()interface{}{
	old:=*h
	n:=len(old)
	x:=old[n-1]
	*h=old[0:n-1]
	return x
}
//自定义 更新数据方法
func (h *intHeap)update(i int,data int)  {
	(*h)[i]=data //将下标为i的元素的数据改为 data
	//只起修复作用,不用以下语句,也可完成修改,不过堆没有进行相应的移位变化(堆的修复)
	heap.Fix(h,0)
}
//自定义 删除数据方法
func (h *intHeap)delete(i int)  {
	heap.Remove(h,i)
}
func main() {
	h:=intHeap{2,1,5}
	heap.Init(&h)  //传入的参数必须是Interface类型,因此需要继承Interface(heap包自定义的)
	heap.Push(&h,3)
	fmt.Println(h)
	h.update(0,33)
	fmt.Println("更新之后的数据是:",h)
	h.delete(0)
	fmt.Println("删除之后的数据是:",h)
	fmt.Println("最小值为:",h[0])
	for len(h)>0{
		fmt.Println(heap.Pop(&h))
	}
	//fmt.Println(h)
}

2、复杂结构的heap使用

package main

import (
	"container/heap"
	"fmt"
)
type Item struct {
	Value    string
	Priority int
	index    int
}
type PriorityQueue []*Item //Item为结构体,值传递,所以要采用指针形式(如果需要进行修改数据的话)

func (p PriorityQueue) Len() int {
	return len(p)
}
func (p PriorityQueue) Less(i, j int) bool {
	return p[i].Priority >= p[j].Priority
}
func (p PriorityQueue) Swap(i, j int) {
	p[i], p[j] = p[j], p[i] //整个数据交换,包括索引
	p[i].index = i          //索引值不交换(代表出堆的顺序)
	p[j].index = j
}
func (p *PriorityQueue) Push(x interface{}) {
	n := len(*p)
	item := x.(*Item)
	item.index = n
	*p = append(*p, item)
}
func (p *PriorityQueue) Pop() interface{} {
	old := *p
	n := len(old)
	item := old[n-1]
	item.index = -1 //已经出堆的数据,索引标注为-1.主要是为了和没有出堆的数据进行区别
	*p = old[:n-1]
	return item
}
func (p *PriorityQueue) Update(item *Item, value string, priority int) {
	//根据传递的参数item来判定修改。(先从堆中找到匹配的 item,并对其进行修改)
	item.Value = value
	item.Priority = priority
	//只起修复作用,不用以下语句,也可完成修改,不过堆没有进行相应的移位变化(堆的修复)
	heap.Fix(p, item.index)  //在修改第i个元素后,调用本函数修复堆,比删除第i个元素后插入新元素更有效率。
}
func (p *PriorityQueue) Delete(item *Item) {
	heap.Remove(p, item.index)
}
func main() {
	pq := make(PriorityQueue, 0)

	heap.Init(&pq)
	item1 := Item{
		Value:    "orange1",
		Priority: 1,
	}
	heap.Push(&pq, &item1)

	item2 := Item{
		Value:    "orange2",
		Priority: 2,
	}
	heap.Push(&pq, &item2)

	item3 := Item{
		Value:    "orange3",
		Priority: 3,
	}
	heap.Push(&pq, &item3)
	item4 := Item{
		Value:    "orange4",
		Priority: 4,
	}
	heap.Push(&pq, &item4)
	fmt.Println("==========Push之后的数据为:===============")
	for i := 0; i < len(pq); i++ {
		fmt.Println(*pq[i])
	}
	pq.Update(&item2, "002", 8) //需要  type PriorityQueue[]*Item
	fmt.Println("==================修改之后的数据为:===================")
	for i := 0; i < len(pq); i++ {
		fmt.Println(*pq[i])
	}
	pq.Delete(&item2)
	fmt.Println("=================删除之后的数据为:=====================")
	for i := 0; i < len(pq); i++ {
		fmt.Println(*pq[i])
	}
	fmt.Println("=================POP数据:=====================")
	for pq.Len() > 0 {
		item := heap.Pop(&pq).(*Item)
		fmt.Println(item.Priority, "\t", item.Value)
	}
}

特别说明,若对应的数据是struct,要采用指针类型(地址传递),才能保证数据修改、删除操作的准确性。

猜你喜欢

转载自blog.csdn.net/weixin_42117918/article/details/90737466