堆排序
参考书《算法导论》
排序是算法中最为基础的问题,这篇文章是关于堆排序算法的理论和python实现
堆排序
在算法导论(一)中有两种排序方法,分别是归并排序和插入排序。其中归并排序有较好的渐近运行时间,而插入排序有空间原址性。堆排序综合了这两种排序的优点。
空间原址性
空间原址性是指,假设对一个序列进行排序,保存在序列外的数是常数级的,就称其具有空间原址性。
1 堆(heap)
堆是一种特殊的数据结构。一般下常常看作二叉树的数组对象,此时也叫二叉堆。
堆的结构有如下一些性质:
- 父节点的下标是i时,左子节点下标为2i,右子节点为2i+1
- 子节点序下标是i时,父节点等于[ i/2 ]向下取整
二叉堆可以分为两种形式:
- 最大堆
- 要求:任意子节点都不大于父节点
- 最小堆
- 要求:任意子节点都不小于父节点
在堆排序中使用最大堆,最小堆常常用于优先队列的构造。
数据结构
将若干数据以某种方式进行组织的方法。
2 堆排序算法
通过函数max-heapify来维护最大堆的性质。这个函数的时间复杂度是 l g ( n ) lg(n) lg(n)
python实现
def max_heapify(A,i,heap_size):
l=i<<1 #好的实现应该要用宏或内联函数 #左移1位乘2
r=l+1
if l<=heap_size and A[l-1]>A[i-1]:
largest=l
else: largest=i
if r<=heap_size and A[r-1]>A[largest-1]:
largest=r
if largest!=i:
A[i-1],A[largest-1]=A[largest-1],A[i-1]
max_heapify(A,largest,heap_size) #递归调用
利用这个函数可以维护最大堆父节点比子节点大的性质。
然后利用max_heapity可以写出从一个序列构建最大堆的函数
python实现
def build_max_heap(A):
heap_size=len(A)
P=A[:]
for i in range(int(heap_size/2),0,-1):
max_heapify(P,i,heap_size)
return P
利用最大堆实现排序算法,因为每次最大堆的根节点,一定是当前序列中最大的数,所以每次从根节点取出数来,然后在维护最大堆性质,再取,就可以把所有数排完。
def heapsort(A):
P=build_max_heap(A)
heap_size=len(P)
for i in range(len(P),0,-1):
P[0],P[i-1]=P[i-1],P[0]
heap_size=heap_size-1 #每取出一个数,heap大小减1
max_heapify(P,1,heap_size)
return P
打印结果
print(v)
print(build_max_heap(v))
print(heapsort(v))
[84, 61, 82, 61, 48, 62, 35, 18, 14, 92]
[92, 84, 82, 61, 61, 62, 35, 18, 14, 48]
[14, 18, 35, 48, 61, 61, 62, 82, 84, 92]
3 利用堆实现优先队列
优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中每个元素都有一个相关值,称为关键字。可以分为最大优先队列和最小优先队列,这里讨论最大优先队列。
队列支持以下操作:
insert(S,x)
:把x插入到集合S中maximum(S)
:返回S中最大键字的元素extract_max(S)
:去掉并返回S中具有最大键字的元素increase-key(S,x,k)
:将元素x的关键字值增加到k,假设k不小于x的原关键字
python 实现
def max_heapify(A,i,heap_size):
l=i<<1 #好的实现应该要用宏或内联函数
r=l+1
if l<=heap_size and A[l-1]>A[i-1]:
largest=l
else: largest=i
if r<=heap_size and A[r-1]>A[largest-1]:
largest=r
if largest!=i:
A[i-1],A[largest-1]=A[largest-1],A[i-1]
max_heapify(A,largest,heap_size) #递归调用
def build_max_heap(A):
heap_size=len(A)
P=A[:]
for i in range(int(heap_size/2),0,-1):
max_heapify(P,i,heap_size)
return P
def heap_maximum(A):
return A[0]
def heap_extract_max(A,heap_size):
if heap_size<1:
print("ERROR")
max1=A[0]
A[0]=A[heap_size-1]
heap_size=heap_size-1
max_heapify(A,1,heap_size)
del(A[heap_size-1])
return max1
def heap_increase_key(A,i,key):
if key<A[i-1]:
print("ERROR")
A[i-1]=key
parent=int(i/2)
while(i>1 and A[parent-1]<A[i-1]):
A[i-1],A[parent-1]=A[parent-1],A[i-1]
i=parent
parent=int(i/2)
def max_heap_insert(A,key):
heap_size=len(A)+1
A.append(-float("inf"))
heap_increase_key(A,heap_size,key)
print("给定序列: "+str(v))
heap=build_max_heap(v)
print("当前最大堆: "+str(heap))
print("返回优先序列中的最大值: "+str(heap_maximum(heap)))
print("当前最大堆: "+str(heap))
print("返回优先序列中的最大值并删除:"+str(heap_extract_max(heap,len(v))))
print("当前最大堆: "+str(heap))
heap_increase_key(heap,5,10000)
print("改写为10000的最大堆: "+str(heap))
max_heap_insert(heap,10002)
print("插入10002的最大堆: "+str(heap))
打印的结果为
给定序列: [33, 52, 91, 5, 85, 90, 51, 71, 57, 48]
当前最大堆: [91, 85, 90, 71, 52, 33, 51, 5, 57, 48]
返回优先序列中的最大值: 91
当前最大堆: [91, 85, 90, 71, 52, 33, 51, 5, 57, 48]
返回优先序列中的最大值并删除:91
当前最大堆: [90, 85, 51, 71, 52, 33, 48, 5, 48]
改写为10000的最大堆: [10000, 90, 51, 71, 85, 33, 48, 5, 48]
插入10002的最大堆: [10002, 10000, 51, 71, 90, 33, 48, 5, 48, 85]
以上就实现来最大优先序列,可以整合成类来使用