zoukankan      html  css  js  c++  java
  • 数据结构和算法-堆

    堆的定义

    • 必须是一个完全二叉树(除了最后一层, 每个节点都有两个子节点, 最后一层只能缺少若干个右节点)
    • 堆中每一个节点的值都必须>=(大顶堆)或<=(小顶堆)左右子树节点的值

    堆中节点的关系

    • 下标为i节点的父节点序号是i/2
    • 下标为i节点的左子树节点时2*i, 右子树节点是2*i+1

    完全二叉树特点:
    如果一个完全二叉树有n个节点, 那么从n/2+1个节点开始到n都是叶子节点

    构造最小二叉堆

    # coding:utf-8
    
    """
    空间复杂度: O(1)  原地排序
    时间复杂度: O(nlogn)
    
    不稳定, 因为最后一个节点跟堆顶节点互换可能导致相同元素的顺序互换
    """
    
    
    class MinHeap(object):
        """
        最小堆
        """
    
        def __init__(self, nums):
            self.heap_list = [0]  # 填充0位置
            self.size = 0
            self.init_heap(nums)
    
        def insert(self, data):
            """
            插入元素, 放到最尾部, 上浮
            :param num:
            :return:
            """
            self.heap_list.append(data)
            self.size += 1
            self._go_up(self.size)
    
        def _go_up(self, i: int):
            """
            末尾元素上浮
            :param i:
            :return:
            """
            while int(i / 2) > 0:
                parent = int(i / 2)
                if self.heap_list[i] < self.heap_list[parent]:
                    self.heap_list[i], self.heap_list[parent] = self.heap_list[parent], self.heap_list[i]
                i = parent
    
        def pop_top(self):
            """
            删除堆顶元素, 使用最后一个值移动到顶部, 再进行下浮
            :return:
            """
            if self.size >= 1:
                top_value = self.heap_list[1]
                self.heap_list[1] = self.heap_list[self.size]
                self.heap_list.pop()
                self.size -= 1
                self._go_down(1)
                return top_value
            else:
                raise Exception("Heap Empty")
    
        def _go_down(self, i: int):
            while (2 * i) <= self.size:
                min_child_pos = self._get_min_child(i)
                if self.heap_list[i] > self.heap_list[min_child_pos]:
                    self.heap_list[i], self.heap_list[min_child_pos] = self.heap_list[min_child_pos], self.heap_list[i]
                i = min_child_pos
    
        def _get_min_child(self, i: int):
            """
            找出i节点左右子树中较小的节点
            :param i:
            :return:
            """
            left = 2 * i
            right = 2 * i + 1
            if right > self.size:
                return left
            elif self.heap_list[left] < self.heap_list[right]:
                return left
            else:
                return right
    
        def init_heap(self, nums: list):
            """
            构造堆. 完全二叉树中从 n/2 开始都是叶子节点, 所以只需要让非叶子节点下沉
            :param nums:
            :return:
            """
            start_pos = len(nums) // 2
            self.size = len(nums)
    
            self.heap_list.extend(nums)
            while start_pos > 0:
                self._go_down(start_pos)
                start_pos -= 1
    
    
    if __name__ == "__main__":
        nums = [9, 4, 7, 1, 8, 20]
    
        mh = MinHeap(nums)
        mh.insert(2)
        mh.insert(17)
    
        res = [mh.pop_top() for _ in range(5)]
        assert res == [1, 2, 4, 7, 8]
    
    

    应用

    优先级队列

    • 高性能定时器
      比如一个定时器中维护了很多的定时任务, 每个任务都设定了一个触发执行的时间点, 定时器每过一个很小的单位时间(比如0.1s), 就会扫描一遍任务, 如果有任务是当前时间, 就触发执行.
      每过0.1s扫描全部任务效率会很低, 所以把所有任务放入一个最小堆中, 堆顶存储的是最先执行任务. 定时器可以根据堆顶任务的执行时间得到一个时间间隔T, 可以直接过T时间后再来检查

    • 爬虫任务的优先队列
      二叉堆常用在爬虫的优先级队列中, 把任务按照优先级放入二叉堆, 调度器可以拿堆顶元素, 保证拿到的是优先级最高的task.

    利用堆求Top K

    • 如何在一个包含n个元素的数组中找出前K大数据?
      构建一个K大小的小顶堆, 遍历数组与堆顶元素比较, 如果比堆顶元素大就删除堆顶数据, 把该数据插入堆中, 否则就比较下一个. 最后得到的小顶堆内的K个元素就是前K大的元素.

    资料

    • <<数据结构和算法-python>>
    • <<大话数据结构>>
    • <<数据结构和算法-王争>>
  • 相关阅读:
    ZipHelper 压缩和解压帮助类
    搜索引擎索引数据结构和算法
    Api之Cors跨域以及其他跨域方式
    UML类图
    NetAnalyzer2016使用方法
    30分钟快速掌握AngularJs
    抖屏与收发各种类型文件
    NET Core RC2
    如何检测被锁住的Oracle存储过程及处理办法汇总(转)
    oracle spfile和pfile文件(转)
  • 原文地址:https://www.cnblogs.com/zlone/p/11043158.html
Copyright © 2011-2022 走看看