什么是堆?

堆是一种完全二叉树(请你回顾下上一章的概念),有最大堆和最小堆两种。

  • 最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property) 最大堆里的根总是存储最大值,最小的值存储在叶节点。
  • 最小堆:和最大堆相反,每个非叶子节点 V,V 的两个孩子的值都比它大。

堆的操作

堆提供了很有限的几个操作:

  • 插入新的值。插入比较麻烦的就是需要维持堆的特性。需要 sift-up 操作,具体会在视频和代码里解释,文字描述起来比较麻烦。
  • 获取并移除根节点的值。每次我们都可以获取最大值或者最小值。这个时候需要把底层最右边的节点值替换到 root 节点之后 执行 sift-down 操作。

堆的表示

上一章我们用一个节点类和二叉树类表示树,这里其实用数组就能实现堆。

仔细观察下,因为完全二叉树的特性,树不会有间隙。对于数组里的一个下标 i,我们可以得到它的父亲和孩子的节点对应的下标:

parent = int((i-1) / 2)    # 取整left = 2 * i + 1right = 2 * i + 2

超出下标表示没有对应的孩子节点。

实现一个最大堆

我们将在视频里详细描述和编写各个操作

class MaxHeap(object):    def __init__(self, maxsize=None):        self.maxsize = maxsize        self._elements = Array(maxsize)        self._count = 0    def __len__(self):        return self._count    def add(self, value):        if self._count >= self.maxsize:            raise Exception('full')        self._elements[self._count] = value        self._count += 1        self._siftup(self._count-1)  # 维持堆的特性    def _siftup(self, ndx):        if ndx > 0:            parent = int((ndx-1)/2)            if self._elements[ndx] > self._elements[parent]:    # 如果插入的值大于 parent,一直交换                self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx]                self._siftup(parent)    # 递归    def extract(self):        if self._count <= 0:            raise Exception('empty')        value = self._elements[0]    # 保存 root 值        self._count -= 1        self._elements[0] = self._elements[self._count]    # 最右下的节点放到root后siftDown        self._siftdown(0)    # 维持堆特性        return value    def _siftdown(self, ndx):        left = 2 * ndx + 1        right = 2 * ndx + 2        # determine which node contains the larger value        largest = ndx        if (left < self._count and     # 有左孩子                self._elements[left] >= self._elements[largest] and                self._elements[left] >= self._elements[right]):  # 原书这个地方没写实际上找的未必是largest            largest = left        elif right < self._count and self._elements[right] >= self._elements[largest]:            largest = right        if largest != ndx:            self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx]            self._siftdown(largest)def test_maxheap():    import random    n = 5    h = MaxHeap(n)    for i in range(n):        h.add(i)    for i in reversed(range(n)):        assert i == h.extract()

实现堆排序

上边我们实现了最大堆,每次我们都能 extract 一个最大的元素了,于是一个倒序排序函数就能很容易写出来了:

def heapsort_reverse(array):    length = len(array)    maxheap = MaxHeap(length)    for i in array:        maxheap.add(i)    res = []    for i in range(length):        res.append(maxheap.extract())    return resdef test_heapsort_reverse():    import random    l = list(range(10))    random.shuffle(l)    assert heapsort_reverse(l) == sorted(l, reverse=True)

Python 里的 heapq 模块

python 其实自带了 heapq 模块,用来实现堆的相关操作,原理是类似的。请你阅读相关文档并使用内置的 heapq 模块完成堆排序。 一般我们刷题或者写业务代码的时候,使用这个内置的 heapq 模块就够用了,内置的实现了是最小堆。

Top K 问题

面试题中有这样一类问题,让求出大量数据中的top k 个元素,比如一亿个数字中最大的100个数字。 对于这种问题有很多种解法,比如直接排序、mapreduce、trie 树、分治法等,当然如果内存够用直接排序是最简单的。 如果内存不够用呢? 这里我们提一下使用固定大小的堆来解决这个问题的方式。

一开始的思路可能是,既然求最大的 k 个数,是不是应该维护一个包含 k 个元素的最大堆呢? 稍微尝试下你会发现走不通。我们先用数组的前面 k 个元素建立最大堆,然后对剩下的元素进行比对,但是最大堆只能每次获取堆顶 最大的一个元素,如果我们取下一个大于堆顶的值和堆顶替换,你会发现堆底部的小数一直不会被换掉。如果下一个元素小于堆顶 就替换也不对,这样可能最大的元素就被我们丢掉了。

相反我们用最小堆呢? 先迭代前 k 个元素建立一个最小堆,之后的元素如果小于堆顶最小值,跳过,否则替换堆顶元素并重新调整堆。你会发现最小堆里 慢慢就被替换成了最大的那些值,并且最后堆顶是最大的 topk 个值中的最小值。 (比如1000个数找10个,最后堆里剩余的是 [990, 991, 992, 996, 994, 993, 997, 998, 999, 995],第一个 990 最小)

按照这个思路很容易写出来代码:

import heapqclass TopK:    """获取大量元素 topk 大个元素,固定内存    思路:    1. 先放入元素前 k 个建立一个最小堆    2. 迭代剩余元素:        如果当前元素小于堆顶元素,跳过该元素(肯定不是前 k 大)        否则替换堆顶元素为当前元素,并重新调整堆    """    def __init__(self, iterable, k):        self.minheap = []        self.capacity = k        self.iterable = iterable    def push(self, val):        if len(self.minheap) >= self.capacity:            min_val = self.minheap[0]            if val < min_val:  # 当然你可以直接 if val > min_val操作,这里我只是显示指出跳过这个元素                pass            else:                heapq.heapreplace(self.minheap, val)  # 返回并且pop堆顶最小值,推入新的 val 值并调整堆        else:            heapq.heappush(self.minheap, val)  # 前面 k 个元素直接放入minheap    def get_topk(self):        for val in self.iterable:            self.push(val)        return self.minheapdef test():    import random    i = list(range(1000))  # 这里可以是一个可迭代元素,节省内存    random.shuffle(i)    _ = TopK(i, 10)    print(_.get_topk())  # [990, 991, 992, 996, 994, 993, 997, 998, 999, 995]if __name__ == '__main__':    test()

python range倒序_Python算法学习之堆和堆排序相关推荐

  1. python堆排序算法_Python算法学习之堆和堆排序

    什么是堆? 堆是一种完全二叉树(请你回顾下上一章的概念),有最大堆和最小堆两种.最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property) ...

  2. Python预测 数据分析与算法 学习笔记(特征工程、时间序列)

    微信公众号:数学建模与人工智能 GitHub - QInzhengk/Math-Model-and-Machine-Learning 第3章 探索规律 3.1 相关分析 相关关系是一种与函数关系相区别 ...

  3. python机器学习 | K近邻算法学习(1)

    K近邻算法学习 1 K近邻算法介绍 1.1算法定义 1.2算法原理 1.3算法讨论 1.3.1 K值选择 1.3.2距离计算 1.3.3 KD树 2 K近邻算法实现 2.1scikit-learn工具 ...

  4. python字典倒序_python字典倒序输出-女性时尚流行美容健康娱乐mv-ida网

    女性时尚流行美容健康娱乐mv-ida网 mvida时尚娱乐网 首页 美容 护肤 化妆技巧 发型 服饰 健康 情感 美体 美食 娱乐 明星八卦 首页 > 高级搜索 python 字典 内置方法ge ...

  5. 算法学习(三)堆排序

    要弄清楚堆排序,我们首先要懂得以下两点: 1)逻辑上的结构,怎么样才是一个堆. 2)存储上的结构,一个堆存储起来的结构是怎么样的. 一般来讲,堆排序中的"堆"指的是二叉堆,一种完全 ...

  6. [算法系列]优先队列,堆与堆排序

    优先队列,堆与堆排序 1 优先队列 有时我们在处理有序元素时,并不一定要求他们全部有序. 很多情况下我们会收集一些元素, 处理当前最大的元素, 然后再收集更多元素, 再处理当前最大元素 - 这种情况下 ...

  7. python算法学习代码_python 算法学习部分代码记录篇章1

    #-*- coding: utf-8 -*-#@Date : 2017-08-19 20:19:56#@Author : lileilei '''那么算法和数据结构是什么呢,答曰兵法''' '''a+ ...

  8. python list倒序_Python基础-不一样的切片操作

    公众号:pythonislover Python切片操作是Python中从列表,字符串等某个序列对象中抽取部分值的情况下使用的方法,首先我们要知道序列对象包括哪些类型. 序列:字符.列表.元组 所有序 ...

  9. python封装举例_Python+Pycharm—学习1—封装导入

    一.封装 目的: 写了一个复杂功能的实现,下次有相同的需求时可以直接导入这个包来使用.就可以达到复用的目的了.通俗的来讲就是打包.(以下用四则运算举例) 方案: 1.1.新建python工程,新建一个 ...

最新文章

  1. Fib(兔子问题)python实现多种方法
  2. python范数norm的计算
  3. 山东大学计算机考博难度,山大博士发11篇SCI,坦言刚读研时自己也很迷茫
  4. 压缩文件目录 linux,linux_压缩文件及目录
  5. 根据BAPI_PO_CREATE1创建采购订单
  6. Backend cache is always enabled
  7. 在数据采集器中用TensorFlow进行实时机器学习
  8. 软件测试中需求分析谁去做的,软件项目在进入需求分析阶段,测试人员应该开始介入其中。 - 问答库...
  9. 启动服务器如何删除文件,在服务器启动时用Filter来删除某种类型的文件
  10. ARM汇编ADR,LDR等伪指令
  11. python中的下标索引
  12. 实现了HTTP多线程下载
  13. set 排序_堆排序C++实现
  14. u大师装iso系统linux,【iso怎么用u盘装系统】iso镜像怎么用u盘装_iso用u盘装系统-系统城...
  15. EDI 公开课:EDI 系统集成之数据库方案
  16. win10磁盘如何解锁bitlocker,解决分区助手无法调整分区问题
  17. 网页视频下载mp4格式到本地
  18. 如何查询计算机上次登录时间,怎么查看电脑的上一次开机时间
  19. 修理牧场(哈夫曼树)
  20. 写文案可以用什么软件?用来写文案的便签应用软件

热门文章

  1. 当前目录未找到系统,请尝试选择更深层的目录再次搜索解决方法
  2. MultiActionController
  3. I2C是什么,什么类型设备支持I2C,为什么要使用它?
  4. android 面向对象,android 面向对象六大原则
  5. java工厂到接口_Java基础——接口简单工厂
  6. 从无到有axure原型设计_Axure|微信原型制作
  7. 指针01:指针的定义与使用
  8. 力扣——字符串转换整数 (atoi)
  9. 竹间智能B轮3000万美元融资,打造业内领先的对话机器人及多模态情感计算平台...
  10. DDD(Domain-Driven Design)领域驱动设计-(三)命令风暴寻找聚合