(10)Go实现二叉堆-数组实现

二叉堆是树结构的一种,它满足以下性质:
(1) 堆中任意节点的值总是不大于(不小于)其子节点的值;
(2) 堆总是一棵完全树;
(3) 节点和节点之间应具有某种可比性
将任意节点不小于/不大于其子节点的堆叫做最大堆/最小堆,图示如下:


最小堆:最小值在顶端
最大堆:最大值在顶端

二叉堆索引之间关系

二叉堆中添加元素的思路,最大堆为例(最大值在最上面):
(1)将元素添加到堆最后,并根据其索引计算出父节点的索引;
(2)比较父节点和该节点,该节点大于父节点,则两者交换位置和索引;
(3)之后根据新索引计算出新的父节点索引,再进行下一轮比较,之后依此类推;

二叉堆中取出元素的思路,最大堆为例(最大值在最上面):
(1)二叉堆每次取出元素都只从root节点取值,这样每次取出的值都是该二叉堆中的最大值;
(2)将root节点取出后,用二叉堆中最后一个节点放到root节点原来的位置,之后做下沉操作;
(3)下沉思路:根据父节点索引计算出左右节点索引,再与左右节点对比,如果父节点小于左右节点中的最大值,则和左右节点中最大值交换位置,之后根据新父节点索引计算新左右节点索引,进行下一轮对比,之后依此类推;
(4)左右节点相等且大于父节点的情况下,优先选择和左节点做换位置;

最大堆的定义和实现:
// 最大堆
type maxHeap struct {
    size int
    nums []int
}

// 获取父节点索引
func parent(i int) int {
    if i == 0 {
        return 0
    }
    return (i - 1) / 2
}

// 获取左节点索引
func leftChild(i int) int {
    return 2*i + 1
}

func rightChild(i int) int {
    return 2*i + 2
}

func NewMaxHeap() *maxHeap {
    return &maxHeap{}
}

func (heap *maxHeap) GetSize() int {
    return heap.size
}

func (heap *maxHeap) IsEmpty() bool {
    return heap.size == 0
}

// 插入元素
func (heap *maxHeap) SiftUp(i int) {
    if heap.size < len(heap.nums) {
        heap.nums[heap.size] = i
    } else { // 扩容
        heap.nums = append(heap.nums, i)
    }
    parI := parent(heap.size)
    childI := heap.size
    for heap.nums[parI] < heap.nums[childI] {
        heap.nums[parI], heap.nums[childI] = heap.nums[childI], heap.nums[parI]
        childI = parI
        parI = parent(parI)
    }
    heap.size++
}

// 下沉操作
func siftDown(heap *maxHeap, parI int) {
    var maxI int
    for {
        leftI := leftChild(parI)
        switch {
        // 左索引超出size
        case leftI+1 > heap.size:
            return

            // 左索引不超,右索引超出size,说明左索引是最后索引
        case leftI+2 > heap.size:
            if heap.nums[parI] < heap.nums[leftI] {
                heap.nums[parI], heap.nums[leftI] = heap.nums[leftI], heap.nums[parI]
            }
            return
        case heap.nums[leftI] >= heap.nums[leftI+1]:
            maxI = leftI
        case heap.nums[leftI] < heap.nums[leftI+1]:
            maxI = leftI + 1
        }

        // 比左右子节点的值都大,返回
        if heap.nums[parI] >= heap.nums[maxI] {
            return
        }

        heap.nums[parI], heap.nums[maxI] = heap.nums[maxI], heap.nums[parI]
        parI = maxI
    }
}

// 取出对中最大元素,即root节点值
func (heap *maxHeap) SiftDown() (int, error) {
    if heap.IsEmpty() {
        return 0, errors.New("failed to siftDown,maxHeap is empty.")
    }
    vTop := heap.nums[0]
    heap.size--
    heap.nums[0], heap.nums[heap.size] = heap.nums[heap.size], 0

    siftDown(heap, 0)

    return vTop, nil
}

// 取出最大元素后,再放入一个新元素
func (heap *maxHeap) Replace(i int) (int, error) {
    if heap.IsEmpty() {
        return 0, errors.New("failed to siftDown,maxHeap is empty.")
    }
    vTop := heap.nums[0]
    heap.nums[0] = i

    siftDown(heap, 0)
    return vTop, nil
}

// 将数值arrs按照二叉堆的定义排序
func Heapify(arrs *[]int) *[]int {
    heap := &maxHeap{
        size: len(*arrs),
        nums: *arrs,
    }
    endParI := parent(heap.size - 1)
    for endParI >= 0 {
        siftDown(heap, endParI)
        endParI--
    }
    return &heap.nums
}

// 打印二叉堆
func (heap *maxHeap) Print() {
    str := ""
    count := 1
    j := 2
    k := 1
    for i := 0; i < heap.size; i++ {
        fmt.Println(str, heap.nums[i])
        count++
        if count == j {
            str = str + "--"
            j = 1 << uint(k)
            k++
            count = 0
        }
    }
}

// 查看堆中最大元素
func (heap *maxHeap) GetMax() (int, error) {
    if heap.IsEmpty() {
        return 0, errors.New("failed to get maxVal,maxHeap is empty.")
    }
    return heap.nums[0], nil
}
测试二叉树
for i := 0; i < 10; i++ {
        h.SiftUp(rand.Intn(20))
    }
    h.Print()

    for i := 0; i < 6; i++ {
        val, err := h.SiftDown()
        fmt.Printf("取出值:%d,  错误:%v\n", val, err)
    }
 19
-- 16
-- 18
---- 7
---- 1
---- 7
---- 5
------ 0
------ 1
------ 0
取出值:19,  错误:
取出值:18,  错误:
取出值:16,  错误:
取出值:7,  错误:
取出值:7,  错误:
取出值:5,  错误:

用二叉堆解决问题:
1)求n个数字中前m个最大值:https://www.jianshu.com/p/29493ea46f7b
2)求n个数字中前m个高频数字:https://www.jianshu.com/p/57918b843a2d

你可能感兴趣的:((10)Go实现二叉堆-数组实现)