一 二叉堆的实现
二叉堆本质上是一种完全二叉数:
- 最大堆:根节点为最大节点,任意的父节点都不小于其所有子节点
- 最小堆:根节点为最小节点,任意的父节点都不大于其所有子节点
二叉堆创建:
根据原数组,然后从最后一个非叶子节点开始,依次下沉,得到最后序列
二叉堆操作:
- 插入节点,位置为最后,然后根据情况上浮调整;
- 删除一般为堆顶的节点,然后最后节点临时补到堆顶位置,然后下沉调整
图比较麻烦就不画了,可以参考这篇文章:二叉堆图解
# 1.二叉堆实现
'''
存储方式:利用数组顺序存储
定位方法:利用数组下标进行定位(父节点Parent,左子树:2*Parent+1,右子树:2*Parent+2)
'''
class MinHeap:
def __init__(self):
self.heap=[]
#上浮调整函数
def shift_up(self):
ChildIndex = len(self.heap)-1
ParentIndex = (ChildIndex-1)//2
#temp 保存插入的子节点的值,用于最后赋值
temp = self.heap[ChildIndex]
while ChildIndex>0 and temp<self.heap[ParentIndex]:
self.heap[ChildIndex] = self.heap[ParentIndex]
ChildIndex = ParentIndex
ParentIndex = (ChildIndex-1)//2
self.heap[ChildIndex] = temp
#下沉调整函数,节点id:ParentIndex
def shift_down(self,ParentIndex):
#temp保存父节点的值,用于最后赋值
temp = self.heap[ParentIndex]
ChildIndex = 2*ParentIndex+1
while ChildIndex<len(self.heap):
#如果有右孩子且左孩子大于右孩子,则定位到右孩子
if ChildIndex+1< len(self.heap) and self.heap[ChildIndex]>self.heap[ChildIndex+1]:
ChildIndex += 1
#如果父节点小于任一子节点,则退出循环
if temp<=self.heap[ChildIndex]:
break
self.heap[ParentIndex] = self.heap[ChildIndex]
ParentIndex = ChildIndex
ChildIndex = ParentIndex*2+1
self.heap[ParentIndex] = temp
#构造推
def buildheap(self,alist):
#从最后一个非叶子节点开始,依次下沉
self.heap = alist
for i in range((len(self.heap)-2)//2,-1,-1):
self.shift_down(i)
#定义插入节点函数,
def insert(self,num):
self.heap.append(num)
self.shift_up()
#删除堆顶元素
def delMin(self):
retrval = self.heap[0]
self.heap[0] = self.heap[len(self.heap)-1]
self.heap.pop()
self.shift_down(0)
return retrval
检验
bh = MinHeap()
bh.insert(1)
bh.insert(8)
bh.insert(4)
bh.insert(3)
bh.insert(2)
bh.heap
[1, 2, 4, 8, 3]
bh1 = MinHeap()
bh1.buildheap([1,8,4,3,2])
bh1.heap
[1, 2, 4, 3, 8]
bh1.insert(5)
bh1.insert(6)
bh1.heap
[1, 2, 4, 3, 8, 5, 6]
bh1.delMin()
# 返回 1
bh1.delMin()
# 返回 2
bh1.heap
# 返回 [3, 5, 4, 6, 8]
时间复杂度分析
插入和删除的操作都是 O(logn)
生成二叉树的操作为 O(n),至于为什么为O(n),可以参照以下文章
https://www.zhihu.com/question/264693363?sort=created
二 堆排序
堆排序利用了二叉堆的堆顶为最大或最小的特性。
算法步骤
- 将无序数组构造成二叉堆。如果需要从小到大排序,则构造最大堆,如果需要从大到小排序,则构造最小堆
- 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
代码实现
可以删除insert和delMin函数,增加Sort函数
#堆排序(降序)
# 生成最小堆
class HeapSort:
def __init__(self):
self.heap=[]
#上浮调整函数
def shift_up(self):
ChildIndex = len(self.heap)-1
ParentIndex = (ChildIndex-1)//2
#temp 保存插入的子节点的值,用于最后赋值
temp = self.heap[ChildIndex]
while ChildIndex>0 and temp<self.heap[ParentIndex]:
self.heap[ChildIndex] = self.heap[ParentIndex]
ChildIndex = ParentIndex
ParentIndex = (ChildIndex-1)//2
self.heap[ChildIndex] = temp
#下沉调整函数,节点id:ParentIndex
def shift_down(self,ParentIndex,length):
#temp保存父节点的值,用于最后赋值
temp = self.heap[ParentIndex]
ChildIndex = 2*ParentIndex+1
while ChildIndex< length :
#如果有右孩子且左孩子大于右孩子,则定位到右孩子
if ChildIndex+1< length and self.heap[ChildIndex]>self.heap[ChildIndex+1]:
ChildIndex += 1
#如果父节点小于任一子节点,则退出循环
if temp<=self.heap[ChildIndex]:
break
self.heap[ParentIndex] = self.heap[ChildIndex]
ParentIndex = ChildIndex
ChildIndex = ParentIndex*2+1
self.heap[ParentIndex] = temp
#构造推
def buildheap(self,alist):
#从最后一个非叶子节点开始,依次下沉
self.heap = alist
for i in range((len(self.heap)-2)//2,-1,-1):
self.shift_down(i,len(self.heap))
def Sort(self,array):
# 1.构造二叉堆
self.buildheap(array)
# 2.循环删除堆顶,移到尾部,调整产生新的堆顶
for i in range(len(self.heap)-1,-1,-1):
temp = self.heap[i]
self.heap[i] = self.heap[0]
self.heap[0] = temp
# 下沉调整堆顶
self.shift_down(0,i)
return self.heap
HS = HeapSort()
HS.Sort([1,5,2,8,4,9,7])
# [9, 8, 7, 5, 4, 2, 1]
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)