作者: 小跑001 | 来源:发表于2020-08-08 15:07 被阅读0次

    1. 最小堆算法

    以前也写过最小堆算法, 但是没什么印象了, 周末想重写下看看. 还是直接看代码吧.

    package main
    
    import "fmt"
    
    type MinHeap struct {
        arrs []int
    }
    
    // 构建最小堆的时间复杂度为O(n)
    func NewMinHeap(arrs []int) *MinHeap {
        mh := &MinHeap{}
        mh.arrs = make([]int, len(arrs))
        copy(mh.arrs, arrs)
    
        for i := len(mh.arrs)/2 - 1; i >= 0; i-- {
            mh.heapify(i)
        }
        return mh
    }
    
    func (mh *MinHeap) heapify(index int) {
        left := 2*index + 1
        right := 2*index + 2
    
        minIndex := index
        if left < len(mh.arrs) && mh.arrs[left] < mh.arrs[minIndex] {
            minIndex = left
        }
    
        if right < len(mh.arrs) && mh.arrs[right] < mh.arrs[minIndex] {
            minIndex = right
        }
    
        if minIndex != index {
            mh.arrs[minIndex], mh.arrs[index] = mh.arrs[index], mh.arrs[minIndex]
            mh.heapify(minIndex)
        }
    }
    
    func (mh *MinHeap) Insert(value int) {
        if mh.arrs == nil {
            mh.arrs = []int{}
        }
    
        mh.arrs = append(mh.arrs, value)
        i := len(mh.arrs) - 1
    
        for {
            parent := (i - 1) / 2
            if parent >= 0 && mh.arrs[i] < mh.arrs[parent] {
                mh.arrs[i], mh.arrs[parent] = mh.arrs[parent], mh.arrs[i]
                i = parent
            } else {
                break
            }
        }
    }
    
    // 注意sort 之后arrs元素为空. 时间复杂度为nlg(n), 这里总觉得时间复杂度为O(n), 推导类似算法导论里面的最小堆构建, 只是相比较而言会比构建多一些步骤(排序包含了构建)
    func (mh *MinHeap) Sort() []int {
        ret := mh.arrs
    
        for len(mh.arrs) > 1 {
            mh.arrs[0], mh.arrs[len(mh.arrs)-1] = mh.arrs[len(mh.arrs)-1], mh.arrs[0]
            mh.arrs = mh.arrs[0 : len(mh.arrs)-1]
            fmt.Printf("test %#v\n", mh.arrs)
            mh.heapify(0)
        }
    
        mh.arrs = nil
    
        return ret
    }
    
    func (mh *MinHeap) ExtractMin() (int, bool) {
        if mh.arrs == nil || len(mh.arrs) == 0 {
            return 0, false
        }
    
        if len(mh.arrs) == 1 {
            ret := mh.arrs[0]
            mh.arrs = nil
            return ret, true
        }
    
        ret := mh.arrs[0]
        mh.arrs[0] = mh.arrs[len(mh.arrs)-1]
        mh.arrs = mh.arrs[0 : len(mh.arrs)-1]
        mh.heapify(0)
        return ret, true
    }
    
    func main() {
        mp := NewMinHeap([]int{100, 30, 200, 29, 70})
        mp.Insert(-3)
        mp.Insert(1000)
        /*
            for {
                value, ok := mp.ExtractMin()
                if !ok {
                    break
                }
                fmt.Println(value)
            }
        */
    
        fmt.Println()
        ret := mp.Sort()
        fmt.Println(ret)
    }
    
    
    • 构建堆说明: 在构建堆的时候, 根据完全二叉树的性质, 由于叶节点没有子节点, 所以天然的满足堆的性质, 调整最小堆从len(mh.arrs)/2 - 1的位置开始.
    • 时间复杂度说明: 构建堆为O(n)的时间复杂度. 堆排序为nlgn的时间复杂度.

    2 堆的应用例子

    2.1 维护流式数据的前K大元素.

    流式数据也就是数据会源源不断的送来, 始终维护前K大的元素. 当元素数量小于K个时候,那么目前为止肯定是前K大的. 当第K+1的个元素来的时候就要判断这个新来的元素和前K的元素的大小关系了. 如果是小于所有元素就可以忽略这个元素, 如果大于前K中最小的元素, 就需要替换这个最小元素. 由这种朴素的思路可以判断我们需要维护前K元素的一个最小元素, 那么最小堆是比较合适的.

    2.2 定时器

    定时器可以通过最小堆维护一系列定时触发事件, 可以通过堆顶来判断有没事件触发, 如果堆顶元素都没到期, 则其它事件就不用判断了. 堆的删除和添加都在lgn时间复杂度, 效率也比较高.

    2.3 排序

    在上面代码里面有堆排序的例子. 相比快排来说, 我认为堆在时间复杂度上更低一些, 因为随着堆排序的进行, 树的高度是不断减小的, 所以时间上是在O(n)和O(nlgn)之间.

    相关文章

      网友评论

          本文标题:

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