python3堆排序_[151225] Python3 实现最大堆、堆排序,解决TopK问题

参考资料:

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

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

大根堆的操作:

插入(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

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()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值