📌算法Go语言描述📌datastruct📌6_heap.go
package datastruct

import (
	"container/heap"
	"fmt"
)

/*
最大堆:
     9
    /\
   5  7
  /\
 1  3
数组表示:[9,5,7,1,3]

下标特点:
根的下标为0,最后一个元素的下标为size-1。
任一结点下标为i,其父结点p下标为(i-1)/2,左子结点l下标为2*i+1,右子结点下r标为2*i+2(即l+1)。

最大堆实现细节(两个操作):
	push:向堆中插入数据时,首先在堆的末尾插入数据,如果该数据比父结点还大,那么交换,然后不断向上提升,直到没有大小颠倒为止。
	pop:从堆中删除最大值时,首先把最后一个值复制到根结点上,并且删除最后一个数值,
		然后和子结点比较,如果值小于子结点则交换,然后不断向下交换,直到没有大小颠倒为止。
		在向下交换过程中,如果有两个子结点都大于自己,就选择较大的。
最大堆有两个核心操作,一个是上浮,一个是下沉,分别对应push和pop。

最大堆从构建到移除,总的平均时间复杂度是:O(nlogn)。

堆数据结构可以用来实现最短路径算法中的优先队列,从而提高算法的效率。
*/

type MaxHeap struct {
	items []int
	size  int
}

func (h *MaxHeap) Size() int {
	return h.size
}

func (h *MaxHeap) IsEmpty() bool {
	return h.size == 0
}

// 最大堆插入元素
func (h *MaxHeap) Push(v int) {
	h.items = append(h.items, v)
	i := h.size // 要插入结点的下标
	for i > 0 {
		p := (i - 1) / 2
		if v <= h.items[p] {
			break // 如果插入的值小于等于父结点,可以直接退出循环
		}
		h.items[i] = h.items[p]
		i = p // 否则将父结点与该结点互换,然后向上翻转,将最大的元素一直往上推
	}
	h.items[i] = v //插入值放在不会再翻转的位置
	h.size++
}

// 最大堆移除根元素,也就是最大的元素
func (h *MaxHeap) Pop() (int, bool) {
	if h.size == 0 {
		return 0, false
	}
	ret := h.items[0] //取出根元素
	h.size--
	v := h.items[h.size] //最后一个值先取出来
	i := 0
	for {
		a := 2*i + 1 // 左子结点下标
		if a >= h.size {
			break // 左子结点越界,表示没有左子结点,也就没有右子结点
		}
		if b := a + 1; b < h.size && h.items[b] > h.items[a] {
			a = b // 此时a为两个子结点中较大结点的下标
		}
		if v >= h.items[a] {
			break // 父结点的值都不小于两个子结点了,不需要再向下翻转
		}
		h.items[i] = h.items[a] // 较大的子结点与父结点交换
		i = a                   // 继续向下翻转
	}
	h.items[i] = v             // 将最后一个元素的值放在不会再翻转的位置
	h.items = h.items[:h.size] // 删除最后一个空位
	return ret, true
}

func TestMaxHeap() {
	list := []int{5, 7, 6, 0, 8, 9, 3, 1, 4, 2}
	h := new(MaxHeap)
	for _, v := range list {
		h.Push(v) // 无序添加进堆
	}
	for h.Size() > 0 {
		fmt.Println(h.Pop()) // 从大到小弹出
	}
}

// "container/heap"包提供了对任意类型(实现了heap.Interface接口)的堆操作。

type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x any) {
	*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() any {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

func IntHeapSort(arr []int) {
	h := IntHeap(arr)
	heap.Init(&h)
	for h.Len() > 0 {
		heap.Pop(&h)
	}
}