目录
定义
二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;
最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
二叉堆一般用数组来表示。如果根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1。因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。
二叉堆的根节点叫做堆顶。
最大堆和最小堆的特点,决定了在最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素
二叉堆虽然是一颗完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组当中。
堆操作
插入节点
在数组的最末尾插入新节点。然后自下而上调整子节点与父节点,比较当前节点与父节点,不满足堆性质则交换。从而使得当前子树满足二叉堆的性质。时间复杂度为。
删除根节点
删除根节点用于堆排序。
对于最大堆,删除根节点就是删除最大值;对于最小堆,是删除最小值。然后,把堆存储的最后那个节点移到填在根节点处。再从上而下调整父节点与它的子节点:对于最大堆,父节点如果小于具有最大值的子节点,则交换二者。这一操作称作down-heap或bubble-down, percolate-down, sift-down, trickle down, heapify-down, cascade-down,extract-min/max等。直至当前节点与它的子节点满足堆性质为止。
构造二叉堆
构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质上就是让所有非叶子节点依次下沉。
一个直观办法是从单节点的二叉堆开始,每次插入一个节点。其时间复杂度为。
最优算法是从一个节点元素任意放置的二叉树开始,自底向上对每一个子树执行删除根节点时的Max-Heapify算法(这是对最大堆而言)使得当前子树成为一个二叉堆。具体而言,假设高度为h的子树均已完成二叉堆化,那么对于高度为h+1的子树,把其根节点沿着最大子节点的分枝做调整,最多需要h步完成二叉堆化。可以证明,这个算法的时间复杂度为O(n)。
合并两个二叉堆
最优方法是把两个二叉堆首尾相连放在一个数组中,然后构造新的二叉堆。时间复杂度为,其中n、k为两个堆的元素数目。
如果经常需要合并两个堆的操作,那么使用二项式堆更好,其时间复杂度为。
堆排序
算法步骤
- 将无序数组构造成二叉堆。如果需要从小到大排序,则构造最大堆,如果需要从大到小排序,则构造最小堆
- 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
代码实现
可以删除insert和delMin函数,增加Sort函数
class MinHeap:
def __init__(self):
self.heap = []
#上浮函数,通过对末尾元素进行上浮,调整堆状态,最终为最小堆
def shift_up(self):
child_index = len(self.heap)-1
father_index = (child_index-1)//2 #整除可以保证无论是左子叶还是右子叶都能定位到父节点
temp = self.heap[child_index]
while child_index > 0 and temp < self.heap[father_index]: #子节点大于父节点,需要上浮
self.heap[child_index] = self.heap[father_index]
child_index = father_index #这是单个节点的上浮操作,所以直接定位到新位置,不考虑其他
father_index = (child_index-1)//2
self.heap[child_index] = temp #循环后赋值,可减少操作,child_index是原fatherindex
#下沉函数,删除根节点后,末尾节点成为根节点,然后执行下沉操作
def shift_down(self,father_index):
child_index = 2*father_index +1 #左孩子
temp = self.heap[father_index] #用于最后赋值,减少操作
while child_index < len(self.heap):
#右子叶最小,根节点右下沉
if self.heap[child_index] > self.heap[child_index+1]:
child_index = child_index +1
#根节点已经最小,不用下沉
if temp < self.heap[child_index]:
break
self.heap[father_index] = self.heap[child_index]
father_index = child_index
child_index = 2*father_index +1 #左孩子
self.heap[father_index] = temp #最后赋值 father_index是原child_index
#下沉函数,删除根节点后,末尾节点成为根节点,然后执行下沉操作
def shift_down_len(self,father_index,length):
child_index = 2*father_index +1 #左孩子
temp = self.heap[father_index] #用于最后赋值,减少操作
while child_index < length:
#右子叶最小,根节点右下沉
if self.heap[child_index] > self.heap[child_index+1]:
child_index = child_index +1
#根节点已经最小,不用下沉
if temp < self.heap[child_index]:
break
self.heap[father_index] = self.heap[child_index]
father_index = child_index
child_index = 2*father_index +1 #左孩子
self.heap[father_index] = temp #最后赋值
#构造堆,从底层开始,对每一个非叶子节点进行下沉
def buildheap(self,alist):
self.heap = alist
for i in range((len(alist)-2)//2, -1,-1): #1,2,3,4,5 (4-1)//2 = 1 (5-2)//2 = 1 4,5的根节点是索引为1 的2
self.shift_down(i)
def insertheap(self,node):
self.heap.append(node)
self.shift_up()
def popheap(self):
temp = self.heap[0]
self.heap[0] = self.heap.pop()
self.shift_down(0)
return temp
def sortheap(self,alist):
self.buildheap(alist)
for i in range(len(alist)-1,-1,-1):
temp = self.heap[0]
self.heap[0] = self.heap[i]
self.heap[i] = temp
self.shift_down_len(0,i)
return self.heap