什么是堆?

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

最小堆:和最大堆相反,每个非叶子节点 V,V 的两个孩子的值都比它大。

堆的操作

堆提供了很有限的几个操作:插入新的值。插入比较麻烦的就是需要维持堆的特性。需要 sift-up 操作,具体会在视频和代码里解释,文字描述起来比较麻烦。

获取并移除根节点的值。每次我们都可以获取最大值或者最小值。这个时候需要把底层最右边的节点值替换到 root 节点之后 执行 sift-down 操作。

堆的表示

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

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

parent = int((i-1) / 2) # 取整

left = 2 * i + 1

right = 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 res

def 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 heapq

class 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.minheap

def 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堆排序算法_Python算法学习之堆和堆排序相关推荐

  1. 【数据结构与算法】三、从堆到堆排序,又是一个logN

    前言: 大家好,我是春风. 今天继续学习简单排序之堆排序,其实相对于堆排序,堆结构可能更加重要~ 一.堆结构 堆其实就是一颗完全二叉树,只是堆的左右没有大小关系,我们一般将一个数组可以转化为堆结构. ...

  2. python range倒序_Python算法学习之堆和堆排序

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

  3. python的networkx 算法_Python NetworkX 学习笔记

    导读热词 chapter1 快速开始 import networkx as nx from matplotlib import pyplot as plt G = nx.Graph() # creat ...

  4. python中难的算法_Python算法很难吗?python神书《算法图解》PDF电子版分享给你

    许多小伙伴后台私信说,python算法让自己很头疼,有没有可以让算法像小说一样有趣的书籍资料呢?看这里吧!小宋为大家找到了这本<算法图解>的PDF电子版!让你在学习python的路上变得轻 ...

  5. python编程的50种基础算法_Python算法新手入门大全

    干货:GitHub标星2.6万!Python算法新手入门大全 Python已经成为最受欢迎的程序设计语言之一.自从2004年以后,python的使用率呈线性增长.2011年1月,它被TIOBE编程语言 ...

  6. python冒泡排序时间复杂度_Python算法中的时间复杂度问题

    在实现算法的时候,通常会从两方面考虑算法的复杂度,即时间复杂度和空间复杂度.顾名思义,时间复杂度用于度量算法的计算工作量,空间复杂度用于度量算法占用的内存空间. 本文将从时间复杂度的概念出发,结合实际 ...

  7. python入门算法_Python 算法入门教程

    分治算法介绍 今天我们聊一聊计算机中非常重要和常用的一种算法:分治算法.它在计算机领域应用广泛,几乎无处不在.不仅计算机领域,在信号处理领域,分而治之也是十分常见的一种信号处理方法.著名快速傅里叶变换 ...

  8. python分治算法_Python算法:分治法

    本节主要介绍分治法策略,提到了树形问题的平衡性以及基于分治策略的排序算法 本节的标题写全了就是:divide the problem instance, solve subproblems recur ...

  9. python分数计算器_python算法——方程计算器小工具

    python算法--方程计算器小工具 工具介绍 方程计算器小工具使用python开发,可实现三元以内一次.二次等方程的计算,包含基本计算器的功能.可用于老师.学生.家长等快速验证方程的求解,检查学生作 ...

最新文章

  1. springboot整合liquibase入门实例
  2. 做好平面设计,你要知道的几个创造性思维?
  3. 华为云服务器配置ipv6,华为云获“IPv6支持度优秀奖”,持续助推云网融合
  4. 安装SandcastleInstaller
  5. 十二、dbms_logmnr(分析重做日志和归档日志)
  6. JavaSE——类集(下)(Set、Comparable、Collections、Comparator、Map)
  7. idea怎么设置代码提示不区分大小写_IntelliJ IDEA 这样设置动图,棒极了!
  8. 「数据分析」Sqlserver中的窗口函数的精彩应用-问题篇
  9. android activity从新打开,【Android开发-8】生命周期,Activity中打开另一个Activity
  10. java 动态实例化对象_记录Java中如何动态实例化一个对象
  11. Atitit.html解析器的选型 jsoup nsoup ,java c# .net 版本
  12. hive优化:大表关联数据倾斜问题
  13. HTML入门之常用标签以及框架写法
  14. 大牛讲解信号与系统(通俗易懂)
  15. 1.10 python 基本数据类型-集合
  16. selenium+cookie自动登录新浪微博(二)
  17. 人体解剖学标本长廊的构成、管理及其优势
  18. 电子小制作:手机控制的收音机
  19. excel之常用vba代码
  20. 判断字符串是否包含在某个集合中

热门文章

  1. php表单数据写入txt文件_vba写入txt数据,发生数据丢失的奇怪现象
  2. 解决使用pip无法安装rasa与无限依赖告警:INFO: This is taking longer than usual. You might need to provide the....
  3. python简体中文、繁体中文转换
  4. html判断字段不为空,js里是否为空字符串的判断
  5. 基于android记事本论文,基于android平台的记事本研究与开发 开题报告
  6. android超级管理员权限作用,Android获取超级管理员权限的实现
  7. w ndows太卡,用Windows 10太卡?教你快速干掉Windows Defender
  8. python log模块_Python日志模块-logging
  9. 2.2 获取图像感兴趣区域_基于Hu距的图像旋转矫正之OpenCV实现
  10. android 浏览器 原理,android 浏览器原理原码