参考资料:

1.算法导论,第6章,堆排序

2. 堆排序学习笔记及堆排序算法的python实现 - 51CTO博客

3. 堆排序 Heap Sort - cnblogs

4. 小根堆实现优先队列:Python实现 -cnblogs

大(小)根堆:是完全二叉树,也是大(小)根树。

大小根堆的差异,主要表现在 比较函数的差异上。

大根堆的操作:

插入(nlog(n)):
概述:把新元素val作为新节点,沿着新节点到根节点的路径,执行一趟冒泡排序。
即:将新元素与父节点的元素进行比较交换,直到父节点不小于子节点为止。 删除(nlog(n)):
目的:删除最大值即根节点root。
(1)首先换首尾节点,然后删除尾结点;
(2)并从根节点出发,进行堆的维护(重构),使堆满足大小次序。 完全二叉树转化为大根堆:
(1)从最后一个具有孩子节的节点开始检查。
(2)如果以该元素为根的子树不是大根堆,进行堆的维护,将该子树调整为大根堆。
(3)依次检查i-1,i-2等节点为根的子树,直到到达树根为止。 堆的维护:
目的:将以当前节点为根节点的子树调整为大根堆。
(1)首先找出当前节点和它的左右孩子节点中的最大值。
maxnode = max(curr,curr_leftchild,curr_right_child)
(2)如果最大节点不是当前节点,则进行交换,并对最大节点开始的子树进行堆维护。

应用:

堆排序:

(1)可以使用优先队列构建堆,然后依次弹出即可。
(2)可以构建优先队列。然后将堆的根(最值)与最右子节点互换,并将堆容量减一。并继续维护,直到容量为2。 已维护的堆的根是最值。。。然后与尾部的进行交换,容量减一,继续维护 TopK问题: (1)使用小根堆记录前K个最大值。
(2)如果新元素大于堆顶,则移除堆顶,并插入新元素。然后进行堆排序/或构建堆!保证正确性

遇到的问题:

循环次数、循环终止条件、循环不变式。
循环和迭代的方式,重写堆维护程序。
大小排序时,注意比较函数别搞混。写出来!!!
程序是让人看的,不要为了优化而优化。
一口吃不了一个胖子,程序应该逐步迭代。 什么时候用@property,什么时候不用?
# !/usr/bin/env python3
# encoding:utf8 left = lambda i:i*2+1
right = lambda i:i*2 +2
parent = lambda i:(i-1)//2 # 与右操作数进行比较
def less(x,y): return x < y
def greater(x,y): return x > y class MyHeap(object):
def __init__(self, l=None,IS_MIN_HEAP=True):
'''初始化
1.如果有数据,初始化数据,并用数据构建堆
2.初始化大根堆或小根堆的比较函数 '''
self._heap=[]
self.cmp = less if IS_MIN_HEAP else greater
if l is not None:
self._heap=list(l)
self.build_heap() #什么时候加@property,什么时候不加?
def top(self):
'''返回堆顶'''
return self.heap[0] @property
def heapsize(self):
'''返回堆的大小'''
return len(self.heap) @property
def heap(self):
'''返回堆的内容'''
return self._heap def __swap(self,i,j):
'''交换以i和j为下标的元素'''
self.heap[i], self.heap[j] = self.heap[j], self.heap[i] def build_heap(self):
'''构建堆
从有叶子节点的最大序号的内部节点往前开始,
对每一个节点进行维护
'''
curr_pos = parent(self.heapsize -1)
max_pos = self.heapsize #从最后一个具有孩子节点的节点(heapsize-1)//2 开始往根调整,构建大根堆
while curr_pos>=0: # 共循环 parent(self.heapsize -1) 次
self.heapify(curr_pos,max_pos)
curr_pos -= 1 def heapify1(self,curr_pos,max_pos):
'''递归的形式,将当前节点为根节点的子树的转为堆
[curr_pos,max_pos)
'''
#最大/最小节点,左孩子,右孩子
mm_pos,lc,rc = curr_pos,left(curr_pos),right(curr_pos) #小根堆比较
#if lc < max_pos and self.heap[lc] < self.heap[mm_pos]:
if lc < max_pos and self.cmp(self.heap[lc], self.heap[mm_pos]):
mm_pos = lc
#if rc < max_pos and self.heap[rc] < self.heap[mm_pos]:
if rc < max_pos and self.cmp(self.heap[rc], self.heap[mm_pos]):
mm_pos = rc # 当最值节点不等于当前节点时,交换节点值,递归维护
if mm_pos != curr_pos:
self.__swap(curr_pos,mm_pos)
self.heapify(mm_pos,max_pos) def heapify(self,curr_pos,max_pos):
'''循环的形式,将当前节点为根节点的子树的转为堆
[curr_pos,max_pos)
''' mm_pos = curr_pos
lc,rc = left(curr_pos),right(curr_pos)
while lc <max_pos:
if lc < max_pos and self.cmp(self.heap[lc], self.heap[mm_pos]):
mm_pos = lc
if rc < max_pos and self.cmp(self.heap[rc], self.heap[mm_pos]):
mm_pos = rc
if mm_pos != curr_pos:
self.__swap(curr_pos,mm_pos)
curr_pos = mm_pos
lc,rc = left(curr_pos),right(curr_pos)
else:
break def push(self,v):
'''插入元素
插入新元素到尾部,并从下往上起泡排序
'''
self.heap.append(v)
curr_pos = self.heapsize - 1
par_pos = parent(curr_pos)
#小根堆比较
#while curr_pos >= 0 and self.heap[curr_pos] < self.heap[par_pos]:
while curr_pos >= 0 and self.cmp(self.heap[curr_pos], self.heap[par_pos]):
self.__swap(curr_pos,par_pos)
curr_pos,par_pos = par_pos,parent(par_pos)
self.heapify(0,self.heapsize) def pop(self):
'''删除元素
1.弹出最值( 首先交换首尾,然后弹出尾部)
2.从根节点维护堆的结构
'''
if self.heapsize == 0:
raise (IndexError,'pop from empty heap')
self.__swap(0,-1)
mv = self.heap.pop()
self.heapify(0,self.heapsize)
return mv def show(self):
'''输出堆信息,注意是按照树有序,不是按行有序'''
print(self.heap) class MinHeap(MyHeap):
def __init__(self,l):
MyHeap.__init__(self,l,IS_MIN_HEAP=True) class MaxHeap(MyHeap):
def __init__(self,l):
MyHeap.__init__(self,l,IS_MIN_HEAP=False) def getTopK(lst,topK):
'''TopK的计算
(1)对前TopK个元素,使用小根堆保存
(2)对后面的元素,依次取出新元素。如果比堆的最小值(top)大,则弹出堆顶,并插入该元素!
'''
if len(lst) < topK:
return None
#前topK个构成小根堆
minheap = MinHeap(lst[:topK])
#后面的逐个进行筛选操作
for v in lst[topK:] :
if minheap.top() < v:
print(minheap.top())
minheap.pop()
minheap.push(v)
minheap.build_heap()
return minheap.heap def HeapSort(lst): def heapify(lst,curr_pos,max_pos):
'''递归的形式,将当前节点为根节点的子树的转为堆
[curr_pos,max_pos)
'''
#左孩子,右孩子,最大/最小节点
mm_pos,lc,rc = curr_pos,left(curr_pos),right(curr_pos)
if lc < max_pos and lst[lc] < lst[mm_pos]:
mm_pos = lc
if rc < max_pos and lst[rc] < lst[mm_pos]:
mm_pos = rc
# 当最值节点不等于当前节点时,交换节点值,递归维护
if mm_pos != curr_pos:
lst[curr_pos],lst[mm_pos] = lst[mm_pos],lst[curr_pos]
heapify(lst,mm_pos,max_pos) curr_pos = (len(lst)-1)//2
max_pos = len(lst)
#从最后一个具有孩子节点的节点(heapsize-1)//2 开始往根调整,构建大根堆
while curr_pos>=0: # 共循环 parent(self.heapsize -1) 次
heapify(lst,curr_pos,max_pos)
curr_pos -= 1 # ## 当用于排序时,添加上一下的语句。注意,需要保证不再进行插入运算?!反正顺序刚反过来
# #已维护的堆的根是最值。。。然后与尾部的进行交换,容量减一,继续维护
while max_pos > 1: #共循环 self.heapsize-1 次
lst[0],lst[max_pos-1] = lst[max_pos-1],lst[0] #堆首尾交换
max_pos -= 1 #容量减去1
heapify(lst,0, max_pos) #维护堆 return lst def test():
lst=[1,23,-6,9,7]
lst=[1,23,-6,9,7,-2,4,5] print(lst) for i in range(1,8):
print("Top{}:{}".format(i,getTopK(lst,i))) print("小根堆:")
mpq = MinHeap(lst)
mpq.show()
for i in range(len(lst)):
print(mpq.pop(),)
print("\n\n") print("大根堆:")
mpq = MaxHeap(lst)
mpq.show()
for i in range(len(lst)):
print(mpq.pop(),)
print("\n\n") print(HeapSort(lst))
print("Done!") if __name__=='__main__':
test()

[151225] Python3 实现最大堆、堆排序,解决TopK问题的更多相关文章

  1. Java解决TopK问题(使用集合和直接实现)

    在处理大量数据的时候,有时候往往需要找出Top前几的数据,这时候如果直接对数据进行排序,在处理海量数据的时候往往就是不可行的了,而且在排序最好的时间复杂度为nlogn,当n远大于需要获取到的数据的时候 ...

  2. 基于PriorityQueue(优先队列)解决TOP-K问题

    TOP-K问题是面试高频题目,即在海量数据中找出最大(或最小的前k个数据),隐含条件就是内存不够容纳所有数据,所以把数据一次性读入内存,排序,再取前k条结果是不现实的. 下面我们用简单的Java8代码 ...

  3. 分治思想--快速排序解决TopK问题

    ----前言 ​ 最近一直研究算法,上个星期刷leetcode遇到从两个数组中找TopK问题,因此写下此篇,在一个数组中如何利用快速排序解决TopK问题. 先理清一个逻辑解决TopK问题→快速排序→递 ...

  4. 如何解决TOP-K问题

    前言:最近在开发一个功能:动态展示的订单数量排名前10的城市,这是一个典型的Top-k问题,其中k=10,也就是说找到一个集合中的前10名.实际生活中Top-K的问题非常广泛,比如:微博热搜的前100 ...

  5. 使用加强堆结构解决topK问题

    作者:Grey 原文地址: 使用加强堆结构解决topK问题 题目描述 LintCode 550 · Top K Frequent Words II 思路 由于要统计每个字符串的次数,以及字典序,所以, ...

  6. 最大堆 最小堆 解决TOPK问题

    堆:实质是一颗完全二叉树,最大堆的特点:父节点值均大于子节点:最小堆的父节点值均小于子节点: 一般使用连续内存存储堆内的值,因而可以根据当前节点的索引值推断子节点的索引值: 节点i的父节点为(i-1) ...

  7. [数据结构]——堆(Heap)、堆排序和TopK

    堆(heap),是一种特殊的数据结构.之所以特殊,因为堆的形象化是一个棵完全二叉树,并且满足任意节点始终不大于(或者不小于)左右子节点(有别于二叉搜索树Binary Search Tree).其中,前 ...

  8. 关于堆排序和topK算法的PHP实现

    问题描述 topK算法,简而言之,就是求n个数据里的前m大个数据,一般而言,m<<n,也就是说,n可能有几千万,而m只是10或者20这样的两位数. 思路 最简单的思路,当然是使用要先对这n ...

  9. scala写算法-用小根堆解决topK

    topK问题是指从大量数据中获取最大(或最小)的k个数,比如从全校学生中寻找成绩最高的500名学生等等. 本问题可采用小根堆解决.思路是先把源数据中的前k个数放入堆中,然后构建堆,使其保持堆序(可以简 ...

随机推荐

  1. SQL语法集锦一:SQL语句实现表的横向聚合

    本文转载:http://www.cnblogs.com/lxblog/archive/2012/09/29/2708128.html 问题描述:假如有一表结构和数据如下: C1 C2 C3 C4 C5 ...

  2. [Windows] Adobe Photoshop CC 2015官方原版下载 附破解补丁&破解教程

      Photoshop自CS6以后改为CC,目前Photoshop CC 2015是最新版,发布日期为2015年6月. <ignore_js_op> 下载安装主程序: 主程序及补丁下载地址 ...

  3. OpenCV 2.4.9

    2014.4.25 感谢那些參加开发.发送错误报告以及通过其它方式帮助我们的全部人和公司. 源代码如今已经可以从SourceForge和Github上下载了. 2.4.9版本号的帮助文章也更新到如今的 ...

  4. BAE、SAE 与 GAE 对比

    从数据库.应用配置.计费.域名绑定.平台服务对比了 BAE.SAE 以及 GAE 的优劣,最后给出云平台选型的建议. 数据库SAE 不支持 InnoDB(可申请支持),BAE 默认支持. BAE 不支 ...

  5. Lucene.Net+盘古分词

    前言 各位朋友,谢谢大家的支持,由于文件过大,有考虑到版权的问题,故没有提供下载,本人已建立一个搜索技术交流群:77570783,源代码已上传至群共享,需要的朋友,请自行下载! 首先自问自答几个问题, ...

  6. ural 1018 Binary Apple Tree(树形dp | 经典)

    本文出自   http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...

  7. 【linux驱动分析】ioctl函数的使用

    一.用户空间的ioctl     int  ioctl(int fd, unsigned long cmd, void *data); 第一个參数是文件描写叙述符,第二个參数代表传递的命令,它会原样传 ...

  8. iOS-CGContextRef画各种图形例子

    iOS-CGContextRef画各种图形例子 绘制效果图 绘制代码 - (void)drawRect:(CGRect)rect { //一个不透明类型的Quartz 2D绘画环境,相当于一个画布,你 ...

  9. HDU 5592 ZYB's Premutation(树状数组+二分)

    题意:给一个排列的每个前缀区间的逆序对数,让还原 原序列. 思路:考虑逆序对的意思,对于k = f[i] - f[i -1],就表示在第i个位置前面有k个比当前位置大的数,那么也就是:除了i后面的数字 ...

  10. PIL安装记录,编译支持jpeg png

    PIL是python理想的图片处理module,但是想要良好的支持各种图片,还需要检查一下几步,否则会提示:IOError: decoder jpeg not available之类的. 我的环境:L ...