python数据结构学*笔记-2016-11-30-01-堆

? ? ? ? 13.4 堆


? ? ? ? 堆(heap)是一个完全二叉树,其结点组织是基于各自结点的数据域。它有两个重要的变种??最大堆和最小堆。


? ? ? ? 最大堆(max-heap),具有称为堆序性质(heap order property)。对于其任意一个内结点而言,该结点处的值均大于其两个子结点的值。


? ? ? ? 最小堆(min-heap),则具有相反的堆序性质,对于其任意一个内结点而言,该结点处的值均小于其两个子结点的值。



? ? ? ? ?13.4.1 定义


? ? ? ? ? 插入操作


? ? ? ? ? 向堆中插入新值,堆序性质和堆形性质(heap shape property,即保持其作为完全二叉树的性质)都要保持。


? ? ? ? ? 例如分别向如下两个最大堆插入元素90和元素41。




? ? ? ? ? 我们从堆的底部开始,先创建新结点,再将之添加到堆中,再与其父结点比较,若大于父结点,则两者互换,重复此过程,直至小于父结点为止。向上移动的过程就称为sift-up过程。




? ? ? ? ? ? ? ? 提取


? ? ? ? ? 从堆中提取元素,即从堆中删除元素。通常,我们是从最大堆中提取最大的元素,从最小堆中提取最小的元素。例如从如下最大堆中提取最大值。




? ? ? ? 首先是复制根结点,其次是最底层最右边的元素,复制到根结点中,然后与其子结点比较,若小于子结点,则将其与较大的子结点互换,重复此过程,直至其到达叶结点或者碰到比其更小的结点为止。向下移动的过程称为sift-down。




? ? ? ? 13.4.2 实现


? ? ? ? 虽然堆本质是二叉树,但是几乎不用动态链式结构来实现,其原因是堆涉及到的向上移动和向下移动,即sift-up和sift-down过程。我们使用数组或者动态数组(python列表)来实现堆。


? ? ? ? 访问结点


? ? ? ? 由于堆是完全二叉树,其中不会有空洞致使内结点缺失。因此可以将根结点放入数组中的索引为0的位置,其两个子结点则分别放入索引为1和2的位置,依次类推。则对于数组中索引为i的位置储存的结点,有


? ? ? ? 父结点的索引为 parent = (i - 1) / 2


? ? ? ? 左子结点的索引为 left = 2 * i + 1


? ? ? ? 右子结点的索引为right = 2 * i + 2


? ? ? ? 判读一个结点是否有子结点,则可计算其假设左子结点和右子结点的索引值,若没有超出数组容量,则相应的子结点存在,若超出数组容量,则相应的子结点就不存在。



#-*-coding: utf-8-*-

# 用数组实现最大堆

class MaxHeap(object):
# 创建最大堆,使用最大容量为maxSize的数组
def __init__(self, maxSize):
self._elements = Array(maxSize)
self._count = 0 # 当前最大堆中的结点个数

# 最大堆中的结点数目
def __len__(self):
return self._count

# 最大堆的容量
def capacity(self):
return len(self._elements)

# 向最大堆中添加元素
def add(self, value):
assert self._count < self.capacity(), "Cannot add to a full heap."
self._elements[self._count] = value
self._count += 1
self._siftUp(self._count-1) # sift-up过程

# 从最大堆中提取最大元素,即根结点
def extract(self):
assert self._count > 0, "Cannot extract from an empty heap."
value = self._elements[0]
self._count -= 1
self._elements[0] = self._elements[self._count]
self._siftDown(0) # sift-down过程

# sift-up过程
def _siftUp(self, ndx):
if ndx > 0:
parent = ndx / 2
if self._elements[ndx] > self._elements[parent]:
self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx]
self._siftUp(parent)

# sift-down过程
def _siftDown(self, ndx):
left = 2 * ndx + 1
right = 2 * ndx + 2
# 找出两子结点中的较大者
largest = ndx
if left < count and self._elements[left] >= self._elements[largest]:
largest = left
elif right < count and self._elements[right] >= self._elements[largest]:
largest = right
# 如果ndx结点的值小于两子结点中的较大者,即largest,则将两者互换
if largest != ndx:
self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx]
self._siftDown(largest)








? ? ? ? 分析


? ? ? ? 在最坏情况下,向最大堆中添加元素,其需要O(log n)的时间,因为在sift-up过程中,结点可能从叶结点处,移至向上移动至根结点处,总共移动log n次,每一次移动所需时间为O(1),所以向最大堆中添加元素的时间复杂度为O(log n)。类似的,从最大堆中提取最大元素的时间复杂度也是O(log n)。






? ? ? ? 13.4.3 优先级队列


? ? ? ? 之前的无界优先级队列,也可以使用最小堆来实现。







? ? ? ? ? 使用最小堆来实现的无界优先级最小堆,其出队和入队的时间复杂度均为O(log n)。


实现最坏情况最坏情况均摊成本均摊成本
?入队出队入队出队
python列表O(n)O(n)O(1)O(n)
链表O(1)O(n)--
最小堆(数组)O(log n)O(log n)--
最小堆(python列表)O(n)O(n)O(log n)O(log n)






相关文档

  • 漫画新皇帝观后感
  • 提升团队业绩必做的事
  • 远志的食用方法
  • kvm linux 网络不稳定,kvm 虚拟机网络配置有时会报错问题
  • 什么耳什么铃的成语
  • 小学新西兰留学申请对于体检你知道多少?
  • 我最怕狗
  • 双肩包带子怎么穿图解
  • java 高性能web_高性能web架构原则
  • 高职高专学生优秀自我鉴定
  • 电子版公司借款合同样书
  • 七夕节酒吧活动策划
  • 脑筋急转弯大全及答案超级爆笑
  • 1000+常用Python库大全,太实用了!
  • 国旗下的讲话演讲稿最新汇总5篇
  • 睡前小故事精选儿童喜欢听的
  • 清洁生产宣传的标语
  • 一种快速统计SQL Server每个表行数的方法
  • dp+贪心+滚动数组优化??植物大战僵尸
  • 【暑假升格赛】情有独钟
  • 多喝咖啡对身体有害吗哪些人尽量不要喝咖啡
  • 不拘小节同义词
  • 有关于中药学的求职信范文
  • 一个程序员老总的年终总结2010版
  • Ubuntu 16.04编译安装最新的OpenCV4.4.0
  • pp苹果助手电脑版授权失败怎么办
  • 男人血尿是什么原因引起的
  • BUG记录-SpringBoot找不到jaxb相关jar包
  • 石家庄推荐国庆免费的旅游景点
  • 用颜真卿的字体书写劝学图片
  • 电脑版