【专题讲解】手撕堆

引入问题是leetcode347题,前K个高频元素
在这里插入图片描述说一千到一万还是一个堆问题。虽然python有堆函数可以直接调用。但是我们还是学习一下利用堆实现优先队列的方法。

堆性质:

  • 堆必须是完全二叉树(保证利用数组存储堆时,由下标i快速找到父节点(i−1)/2,左右子节点2∗i+1,2∗i+2。(从0开始存储)

  • 堆中每个节点必须大于等于(小于等于)子节点

  • 支持插入、删除堆顶元素(并且要堆化)

  • 如果数组存储n个元素,最后一个非叶子节点下标为n/2−1

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # hashmap 统计频率
        freq_count = {}
        for num in nums:
            if num in freq_count:
                freq_count[num] += 1
            else:
                freq_count[num] = 1

        def sift_up(arr, k):
            """ 时间复杂度 O(logk) k 为堆的规模"""
            new_index, new_val = k-1, arr[k-1]
            while (new_index > 0 and arr[(new_index-1)//2][1] > new_val[1]):
                arr[new_index] = arr[(new_index-1)//2]
                new_index = (new_index-1)//2
            arr[new_index] = new_val # 这里采用的是类似插入排序的赋值交换

        def sift_down(arr, root, k):
            """ O(logk). 左节点index 2*root+1, 右节点 2*root+2, 父节点 (child-1)//2"""
            root_val = arr[root]
            while (2*root+1 < k):
                child = 2 * root + 1
                # 小顶锥 用 >,大顶锥 用 <
                if child+1 < k and arr[child][1] > arr[child+1][1]:
                    child += 1
                ## 两个子节点中小与根节点比较
                if root_val[1] > arr[child][1]:
                    arr[root] = arr[child]
                    root = child # 继续向下检查
                else: 
                    break # 如果到这里没乱序,不用再检查后续子节点
            ## 新数值下沉到该位置
            arr[root] = root_val

        # 注意构造规模为k的堆, 时间复杂度O(n),因为堆的规模是从0开始增长的
        freq_list = list(freq_count.items())
        # print(freq_list) [(1, 3), (2, 2), (3, 1)]第一项是key,第二项freq
        ## 采用小顶堆,确定了kk个以后,只要又大于堆顶的入堆,堆顶移除
        min_heap = []
        for i in range(k):
            min_heap.append(freq_list[i])
            sift_up(min_heap, i+1)

        # 遍历剩下元素,大于堆顶入堆,下沉维护小顶堆
        for item in freq_list[k:]:
            priority = item[1]
            if priority > min_heap[0][1]:
                min_heap[0] = item
                sift_down(min_heap, 0, k)

        return [item[0] for item in min_heap]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值