参考资料:

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. hadoop单线程实现server多socket连接读取数据原理分析

    一.问题引出. Hadoop 的Server 采用了Java 的NIO,这样的话就仅需要为每一个socket 连接建立一个线程,读取socket 上的数据.在Server 中,只需要一个线程,就可以a ...

  2. hdu 3720 Arranging Your Team 枚举

    不可能解可以直接判断. 搭配产生的附加分可以用一个二维数组保存. 枚举1442,4种类型的人,因为总人数只有23个,所以可以搜索暴力枚举,然后保存最优解. 注意trick,答案可能为负数,所以初始化a ...

  3. android ScrollView 充满屏幕

    android:fillViewport=true ScrollView下面的组件如果有android:layout_height="fill_parent"或android:la ...

  4. alloc、init你弄懂50%了吗?

    前言 这是一篇我记录对alloc.init分析思考的笔记.如果读者想看懂我的第二个思考,可能需要您至少了解内存的分段分页管理,如果您对其一点都不知道,可以先看这篇软文简单了解一下.另外很重要的一点是, ...

  5. WWDC-UIKit 中协议与值类型编程实战

    本文为 WWDC 2016 Session 419 的部分内容笔记.强烈推荐观看. 设计师来需求了 在我们的 App 中,通常需要自定义一些视图.例如下图: 我们可能会在很多地方用到右边为内容,左边有 ...

  6. webapp思路和rem适配极其viewport

    webapp在制作时候,页面上要加入viewport标签,用来进行适配; viewport的meta标签,指的是在移动端显示的时候,viewport是多大?移动端的浏览器是屏幕宽,viewport一般 ...

  7. asp.net对word文档进行修改 对于使用word文档做模板编辑比较适用

    最近做项目,需要多word文档进行编辑并导出一个新的word,在最初的word编辑中留下特定的字符串用来替换,然后在本地生成一个新的word文档,并且不修改服务器中的word文档,这样才能保证服务器中 ...

  8. CI 笔记(1)

    1. 下载CI,官方网站,目前3.x版本已经更新,2.2.6版本为2.x版本的最后的一个版本.为了和视频教材一致,使用CI 2.x版本 2. 目录结构,从application里面的,controll ...

  9. iOS中常用的正则表达式

    iOS常用正则表达式 正则表达式用于字符串处理.表单验证等场合,实用高效.现将一些常用的表达式收集于此,以备不时之需. 匹配中文字符的正则表达式: [\u4e00-\u9fa5]评注:匹配中文还真是个 ...

  10. TCP/UDP基本概念部分

    最近在读<Unix网络编程>和<TCP/IP详解>两本书,有了一些自己的心得与体会,总结下其中典型的问题. 1. 为什么建立连接需要三次握手? 谢希仁的<计算机网络> ...