一、
首先堆是一棵完全二叉树,关于完全二叉树和满二叉树:
如果一个二叉树的任何节点,要么是叶子节点,要么左右子树均非空,则这棵二叉树称做满二叉树(full binary tree)
如果一个二叉树最多只有最下面的两层节点度数可以小于2,并且最下面一层的节点都集中在该层最左边的连续位置上,则此二叉树称做完全二叉树(complete binary tree)
堆分为大顶堆和小顶堆:
大顶堆就是堆顶(第一个元素)始终存放的是这组元素中的最大元素,小顶堆就是堆顶元素是最小元素。
二、
堆是一颗完全二叉树,它的节点索引规则,关于节点的父节点和子节点在堆中的位置:
-
节点 i 的左子节点为2 * i + 1,右子节点为 2 * i+2
-
节点 i 的父节点为 (i - 1) /2
三、实现(golang)
小顶堆
//堆的常规操作
func (h heap) swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h heap) less(i, j int) bool {
return h[i] < h[j]
}
//插入
func (h *heap) push(x int) {
*h = append((*h), x)
h.up(len(*h) - 1)
}
//交换维护小顶堆
func (h heap) up(i int) {
for {
fn := (i - 1) / 2 //父节点位置
if fn == i || h.less(fn, i) {
break
}
h.swap(fn, i)
i = fn
}
}
/*删除
*把最末端的节点和要删除节点的位置进行交换。
*删除末端节点
*重复上述up过程维护小顶堆
*/
//维护删除时的树,当此节点是叶子节点,或满足最小堆的条件时退出
func (h heap) down(i int) {
for {
//左节点,右节点
ln, rn := 2*i+1, 2*i+2
//如果要删除的节点是叶子节点就退出
if ln >= len(h) {
break
}
//左右节点中的最小节点
least := ln
if rn < len(h) && h.less(rn, least) {
least = rn
}
//此节点与最小节点进行比较
if h.less(i, least) {
break //小于子节点则退出
}
h.swap(i, least) //交换父子节点
i = least //继续向下比较
}
}
// 删除堆中位置为i的元素
// 返回被删元素的值
func (h *heap) remove(i int) (int, bool) {
if i < 0 || i >= len(*h) {
return 0, false
}
n := len(*h) - 1
h.swap(i, n) //将要删除节点与最后的节点进行交换
//删除节点
x := (*h)[n]
*h = (*h)[:n]
//维护最小堆树
//当前节点是根节点或者比父节点小,则向下维护最小堆
if i == 0 || (*h)[i] > (*h)[(i-1)/2] {
h.down(i)
} else {
h.up(i)
}
return x, true
}
//弹出堆顶元素
func (h heap) pop() int {
x, _ := h.remove(0)
return x
}
//构造堆
//从 i = len(arr) /2 开始依次向上调整,
//i = len(arr) /2是堆中末尾节点的父节点, i=0是根节点。
func buildHeap(arr []int) heap {
h := heap(arr)
n := len(arr)
//遍历构造堆
for i := n/2 - 1; i >= 0; i-- {
h.down(i)
}
return h
}
堆排序
//堆排序
func heapSort(arr []int) []int {
heap := buildHeap(arr)
return heap
}
func main() {
arr := []int{9, 8, 7, 6, 5, 4, 2, 12}
fmt.Println(heapSort(arr))
}
四、调用container/heap包
package main
import (
heap2 "container/heap"
"fmt"
)
type H []int
func (h H) Len() int {
return len(h)
}
func (h H) Less(i, j int) bool {
return h[i] < h[j]
}
func (h H) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h *H) Push(x interface{}) {
xh, _ := x.(int)
*h = append(*h, xh)
}
func (h *H) Pop() interface{} {
n := len(*h)
x := (*h)[n-1] //排出堆顶元素
*h = (*h)[0 : n-1]
return x
}
func main() {
var h = &H{3, 4, 6, 9, 1, 2}
heap2.Init(h)
//fmt.Println(h)
for range *h {
fmt.Println(heap2.Pop(h))
}
fmt.Println(h)
}
调用包建立堆需要满足heap包里的接口
type Interface interface {
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}
同时还需要满足sort包里的接口
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)
}
即自定义一共五个方法,由自己选择建立最大堆还是最小堆。升降序由Less()来决定,自定义类也可以直接用Sort来快速排序,因为实现了相关接口