一、什么是堆
1. 定义
设有n个数据元素组成的序列{a1, a2, ..., an},这些数据元素是一棵完全二叉树中的节点,如果对于所有节点,父节点数据总是大于子节点数据,则称该序列为大根堆(最大堆),反之,若父节点数据总是小于子节点数据,则称为小根堆(最小堆)。
2. 应用
一般用于比较快速的取得一个数列的最大或最小值。
二、 堆的实现
由于堆的数据是一棵完全二叉树的节点,所以可以很方便的使用数组进行存储,直接利用下标来表示父节点与子节点之间的关系,下标为i的元素的左子节点的下标为2*i+1,右子节点的下标为2*2+2。
下面是Go语言实现的最小堆的代码,最大堆的实现可以此类推。实现代码的核心部分为向下调整shiftDown()和向上调整shiftUp()两个方法,前者为堆初始化或删除时调用,后者为堆插入时调用。
/**
* 最小堆
* author:JetWu
* date:2020.02.08
*/
package minHeap
import (
"errors"
"fmt"
)
/**
* 最小堆结构
**/
type MinHeap struct {
slice []int
size int //最小堆大小
}
/**
* 创建一个空最小堆
**/
func NewMinHeap() *MinHeap {
return &MinHeap{
slice: make([]int, 10),
size: 0,
}
}
/**
* 使用切片创建一个最小堆
**/
func MakeMinHeap(slice []int) *MinHeap {
mh := &MinHeap{
slice: slice,
size: len(slice),
}
i := (mh.size - 2) / 2 //最后一个拥有子节点的节点
for i >= 0 {
mh.shiftDown(i)
i--
}
return mh
}
/**
* 打印最小堆
**/
func (mh *MinHeap) Print() {
for i := 0; i < mh.size; i++ {
fmt.Print(mh.slice[i], " ")
}
fmt.Println()
}
/**
* 判断最小堆是否为空
**/
func (mh *MinHeap) IsEmpty() bool {
return mh.size < 1
}
/**
* 最小堆容量
**/
func (mh *MinHeap) Count() int {
return mh.size
}
/**
* 向下调整(最小堆初始化、删除时调用)
**/
func (mh *MinHeap) shiftDown(start int) {
i, j := start, start*2+1 //j为i的左子节点
temp := mh.slice[i]
for j < mh.size {
if j < mh.size-1 && mh.slice[j+1] < mh.slice[j] { //比较获得左右子节点的最小值编号
j++
}
if temp <= mh.slice[j] { //左右子节点都比父节点大
break
} else { //继续向下比较
mh.slice[i] = mh.slice[j]
i, j = j, j*2+1
}
}
mh.slice[i] = temp
}
/**
* 向上调整(最小堆插入时调用)
**/
func (mh *MinHeap) siftUp(start int) {
if start > mh.size-1 {
return
}
j, i := start, (start-1)/2 //i为j的父节点
temp := mh.slice[j]
for j > 0 {
if temp >= mh.slice[i] { //子节点大于父节点
break
} else {
mh.slice[j] = mh.slice[i]
j, i = i, (i-1)/2
}
}
mh.slice[j] = temp
}
/**
* 插入
**/
func (mh *MinHeap) Insert(data int) {
//将插入元素放置在最后节点
mh.size++
if mh.size < len(mh.slice) {
mh.slice[mh.size-1] = data
} else {
mh.slice = append(mh.slice, data)
}
//向上调整
mh.siftUp(mh.size - 1)
}
/**
* 删除
**/
func (mh *MinHeap) RemoveMin() (int, error) {
if mh.size < 1 {
return 0, errors.New("最小堆为空")
}
min := mh.slice[0]
mh.size--
//使用最后一个节点替换第一个节点
mh.slice[0] = mh.slice[mh.size]
//向下调整
mh.shiftDown(0)
return min, nil
}
来源:https://www.cnblogs.com/wujuntian/p/12286502.html