美文网首页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欢迎指出,转载请注明出处。

    相关文章

      网友评论

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

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