美文网首页Go数据结构
(10)Go实现二叉堆-数组实现

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

作者: 哥斯拉啊啊啊哦 | 来源:发表于2019-04-22 08:25 被阅读4次

二叉堆是树结构的一种,它满足以下性质:
(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,  错误:<nil>
取出值:18,  错误:<nil>
取出值:16,  错误:<nil>
取出值:7,  错误:<nil>
取出值:7,  错误:<nil>
取出值:5,  错误:<nil>

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

有bug欢迎指出,转载请注明出处。

相关文章

  • (25)Go实现反向索引堆

    普通堆(10)Go实现二叉堆-数组实现:https://www.jianshu.com/p/37bca5f2a6e...

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

    二叉堆是树结构的一种,它满足以下性质:(1) 堆中任意节点的值总是不大于(不小于)其子节点的值;(2) 堆总是一棵...

  • 数据结构 树的应用(1)堆

    堆heap 堆的存储 堆的结构:堆(二叉堆)实际上是完全二叉树,所以可以用数组来实现堆的结构。 便于检索数组下标i...

  • [数据结构与算法-iOS 实现]二叉堆原理实现及top k 问题

    二叉堆 demo 二叉堆其实就是一颗完全二叉树,所以又可以叫做完全二叉堆 二叉堆的底层可以用数组来实现 对于二叉堆...

  • 堆的实现

    本文主要讲堆的具体实现 堆 堆是优先队列的一种实现。堆一般是由数组实现,逻辑上堆可以看作是一棵完全二叉树。即我们本...

  • 「树」,用数组实现就是「堆」,因为「堆」是一个完全二叉树,用数组存储不需要节点指针,操作也比较简单;用链表实现就是...

  • python实现二叉树

    递归实现二叉树 堆实现二叉树前序遍历

  • [AlgoGo]堆

    堆的定义 完全二叉树 每个节点大于等于子节点 堆的实现 存储方式堆是一个完全二叉树,完全二叉树适合时候数组存储,因...

  • 堆(go实现)

  • python数据结构教程 Day13

    本章内容: 利用二叉堆实现优先队列 二叉堆的python实现 二叉查找树及操作 一、优先队列Priority Qu...

网友评论

    本文标题:(10)Go实现二叉堆-数组实现

    本文链接:https://www.haomeiwen.com/subject/icsegqtx.html