一、什么是堆
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 }