首页 > 编程知识 正文

最大堆是什么,最大堆大小

时间:2023-05-05 21:30:03 阅读:237280 作者:109

背景知识:

堆是一棵完全二叉树,什么是完全二叉树?就是除了最后一层节点之外,其他层的节点个数必须是最大值,并且最后一层的节点必须都集中在左侧。堆分为最大堆和最小堆。最大堆就是父节点大于等于子节点,从而导致根节点是最大值。最小堆就是父节点小于等于子节点,从而导致根节点是最小值。本文基于最大堆讲解,并且提供python代码实现。

1.背景知识中提到,堆是一棵完全二叉树,并且最大堆的特点是父节点大于等于子节点,为了更直观的理解什么是最大堆,请看下图:

图A是一个最大堆,首先它满足完全二叉树的性质,即除了最后一层节点数不是最大以外,其他层节点数达到最大,同时最后一层节点都集中在左侧。其次它满足最大的性质,即每一个父节点的值都是大于等于子节点的。

图B不是一个最大堆,因为它不是一棵完全二叉树,因为第二层的节点个数并没有达到最大

图C不是一个最大堆,虽然它除了最后一层其他层的节点个数达到了最大,但是最后一层的节点没有集中在左侧

图D不是一个最大堆,虽然在定义上它是一棵完全二叉树,但是它不满足最大堆每一个父元素的值都大于等于子元素,因为我们可以很明显的看到62是50的右孩子。

2.经过上述的分析,相信读者对于最大堆已经有了很深刻的认识,接着如果我们对最大堆,至上而下从左往右依次从1开始标序,例如:

我们可以发现:左孩子的下标是父亲下标的两倍,右孩子的下标是父亲下标的两倍加一,而父亲下标则是孩子下标除以二取整。基于这个性质我们可以使用数组这种数据结构来存储最大堆。

3.在利用数组这个数据结构构建最大堆的过程称之为Shift Up,什么是Shift Up呢?在数组中添加一个元素之后,根据新添加孩子的下标找到父亲的下标,比较这两个元素的大小,如果父亲的值小于孩子的值,则交换对应的值,接着再向前比较,依次类推,直到父亲的值小于孩子的值时,则停止。具体代码实现:

import randomclass maxStack: def __init__(self): # 将下标为0的元素赋值为0,显示约束下标为0不可用,因为本案例实现的最大堆是从下标为1开始 self.data = [0] self.count = 0 # 第一个元素是从下标为1开始的 def insert(self, item): self.data.append(item) self.count += 1 self.__shiftUp(self.count) # 判断新增的元素是否比父元素大,大的话就交换位置,依次类推 def __shiftUp(self, count): while count > 1 and self.data[count // 2] < self.data[count]: self.data[count // 2], self.data[count] = self.data[count], self.data[count // 2] count = count // 2if __name__ == '__main__': stack = maxStack() for i in range(0, 10): stack.insert(random.randint(10, 100))#经过上述操作之后,stack.data这个列表存储的值就是一个最大堆。

4.我们都知道树结构取值都是取根元素的,同样最大堆也是如此,即下标为1的元素就是根元素。这时我们不得不提一个与Shift Up相对应的操作,Shift Down 操作。什么是Shift Down操作?当我们取出数组的第一个元素即下标为1的元素时,交换第一个元素和最后一个元素的值,删除掉最后的元素之后,这时数组可能不满足最大堆的性质了,怎么解决?从根元素开始,比较两个孩子的大小,较大的孩子再和父元素比较,如果比父元素小,则停止,否则交换位置,依次向下追溯,直至节点为叶子节点(即没有孩子)或者父元素的值大于子元素时停止,具体的代码实现:

import randomclass maxStack: def __init__(self): # 将下标为0的元素赋值为0,显示约束下标为0不可用,因为本案例实现的最大堆是从下标为1开始 self.data = [0] self.count = 0 # 获取元素:堆的元素获取只能获取根节点也就是下标为1的元素,然后将最后的元素挪到根节点,然后count减1相当于移除最后的元素 def getMax(self): if self.count <= 0: return root = self.data[1] self.data[1], self.data[self.count] = self.data[self.count], self.data[1] self.count -= 1 self.__shiftDown(1) return root # 首先比较当前节点的两个子节点的大小,如果大的那个子节点比父元素大就交换位置 def __shiftDown(self, key): # 在完全二叉树中,只要保证有左节点那么这个节点就是有孩子的,因为完全二叉树并不存在有右节点而没有左节点 while 2 * key <= self.count: # 默认change指向左节点 change = 2 * key # 如果存在右节点并且右节点比左节点大,那么将change加1,此时change指向了右节点 if change + 1 <= self.count and self.data[change + 1] > self.data[change]: change += 1 # 经过上面两步,此时change指向的值是key父节点最大的孩子节点 if self.data[change] <= self.data[key]: break self.data[change], self.data[key] = self.data[key], self.data[change] key = change

5.完整的python测试用例:

import random# 最大堆(完全二叉树,并且父元素大于等于子元素)的实现,使用数组的方法实现(python中使用列表)class maxStack: def __init__(self): # 将下标为0的元素赋值为0,显示约束下标为0不可用,因为本案例实现的最大堆是从下标为1开始 self.data = [0] self.count = 0 def is_empty(self): return 0 != self.count def get_size(self): return self.count # 第一个元素是从下标为1开始的 def insert(self, item): self.data.append(item) self.count += 1 self.__shiftUp(self.count) # 判断新增的元素是否比父元素大,大的话就交换位置,依次类推 def __shiftUp(self, count): while count > 1 and self.data[count // 2] < self.data[count]: self.data[count // 2], self.data[count] = self.data[count], self.data[count // 2] count = count // 2 # 获取元素:堆的元素获取只能获取根节点也就是下标为1的元素,然后将最后的元素挪到根节点,然后count减1相当于移除最后的元素 def getMax(self): if self.count <= 0: return root = self.data[1] self.data[1], self.data[self.count] = self.data[self.count], self.data[1] self.count -= 1 self.__shiftDown(1) return root # 首先比较当前节点的两个子节点的大小,如果大的那个子节点比父元素大就交换位置 def __shiftDown(self, key): # 在完全二叉树中,只要保证有左节点那么这个节点就是有孩子的,因为完全二叉树并不存在有右节点而没有左节点 while 2 * key <= self.count: # 默认change指向左节点 change = 2 * key # 如果存在右节点并且右节点比左节点大,那么将change加1,此时change指向了右节点 if change + 1 <= self.count and self.data[change + 1] > self.data[change]: change += 1 # 经过上面两步,此时change指向的值是key父节点最大的孩子节点 if self.data[change] <= self.data[key]: break self.data[change], self.data[key] = self.data[key], self.data[change] key = changeif __name__ == '__main__': stack = maxStack() list = [random.randint(10,100) for i in range(100)] print('原数组:',end=' ') print(list) for i in range(0, 100): stack.insert(list[i]) print('最大堆数组:',end=' ') print(stack.data) # 循环取出堆的元素,可以发现最大堆循环取出的元素是从大到小排序的 while stack.is_empty(): print(stack.getMax(), end=' ')

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。