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,要采用指针类型(地址传递),才能保证数据修改、删除操作的准确性。