[151225] Python3 实现最大堆、堆排序,解决TopK问题
2016-01-11 17:08
573 查看
参考资料:
1.算法导论,第6章,堆排序堆排序学习笔记及堆排序算法的python实现 - 51CTO博客
堆排序 Heap Sort - cnblogs
小根堆实现优先队列: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()
相关文章推荐
- Spark环境下的Kmeans-Python实现
- python豆瓣电影爬虫
- Python篇----命令行调用、输出log日志并打包的脚本
- Python进阶06-10
- [160111] Python学习记录
- Python使用wxPython报错解决方法
- Python转码问题
- [转]python pickle包,cPickle包 存储
- [转]python yield
- Python函数对象
- [转]Python核心模块——urllib模块
- 17个新手常见Python运行时错误
- 4.7.2. Keyword Arguments(关键字参数)
- python split()黑魔法
- python小点心--globals函数
- python爬山算法
- Python堡垒机实现之基础知识
- python enumerate()用法
- python求最优解的集中算法
- Python入门笔记