前言
首先想强调的是一个数组转换成完全二叉树它的结构是唯一的,如下图所示:
[1,2,9,10,8,11]
#堆 Heap,有两种堆:最大堆和最小堆。
#堆应用:堆排序,求前k个大(小)的数,第k个大(小)的数
#堆是完全二叉树的结构 且要满足 堆中的每一个节点要么全部都要大于等于孩子节点(这种堆称为最大堆),要么全部都小于等于孩子节点(这种堆称为最小堆)
#最大堆的最大值是堆顶元素
#最小堆的最小值是堆顶元素
#访问: 无索引所以没有访问这个概念
#搜索: 时间复杂度O(1):堆的搜索是指查看堆顶元素。如果你要在堆里查看其他元素,那么它的复杂度是O(N)
#添加: 时间复杂度O(logN),首先需要知道的是完全二叉树的高度是对log2N向下取整(2是底数)+1,N是节点个数,添加的元素都是先在最后一层的最右边进行添加,保证完全二叉树的成立。比如拿最大堆举例,比如向最大堆添加一个元素,每次要和当前的父节点对比谁大,然后判断是否交换,重复这个过程。可以发现我们每一次只需要和上一层(父亲)进行对比就行,不需要和同层的兄弟姐妹进行对比。
#删除:时间复杂度O(logN),删除一般是指删除堆顶元素。比如拿最大堆举例,在我们删除了最大堆的堆顶元素后,我们首先把二叉树最后一个节点挪到堆顶,这样是为了保证完全二叉树的成立!然后就把堆顶元素和左右孩子进行比较的方式判断是否交换,与最大的那个孩子节点进行交换,重复这个过程。可以发现和添加的操作类似,我们每一次只需要和上一层(父亲)进行对比就行,不需要和同层的兄弟姐妹进行对比。
#创建堆的时间复杂度是O(N),即堆化操作的时间复杂度是O(N),N为节点的个数。堆化:把一组无序的数转换成完全二叉树,然后将完全二叉树变成最大堆或者最小堆。
#为什么呢?首先要知道数组转换成完全二叉树是时间复杂度为O(N),一个数组转换成完全二叉树它的结构是唯一的,所以我们只需要把数组遍历一遍就可以得到完全二叉树的结构。
#然后完全二叉树构造成新的最小堆或者最大堆的时间复杂度是O(N),因为叶子节点(就是最后一层的节点,没有孩子的节点)的数量最多是N/2向下取整加1个(N为总节点数),每往上一层的节点数就缩小2倍,剩下的具体证明要推公式,得到一个等差等比数列。
#拿将完全二叉树变成最小堆举例,中心思想是把每一个节点只和它的孩子节点进行对比,如果两个孩子节点都比它小则选最小的孩子节点进行交换,从最后一层往上开始重复这个过程。
#堆的常用操作:
#创建堆或者叫堆化(最大堆和最小堆)
# 注意: python里没有方法能直接创建最大堆只有直接创建最小堆,
# 所以想创建最大堆时可以先对数组进行取反然后构建得到最小堆,
#每次搜索时堆顶元素乘-1,就是原始最大的那个元素,就是最大堆的感觉了。
import heapq
minheap = []#创建一个列表
heapq.heapify(minheap)# 将列表堆化成最小堆
#添加元素
heapq.heappush(minheap,10)
heapq.heappush(minheap,8)
heapq.heappush(minheap,9)
heapq.heappush(minheap,2)
heapq.heappush(minheap,1)
heapq.heappush(minheap,11)
print(minheap) #[1, 2, 9, 10, 8, 11],这个数组写成堆是唯一的一个形式
#获取堆顶元素
minheap[0]
#删除堆顶元素
heapq.heappop(minheap)
#堆的长度
len(minheap)
#堆的遍历(边删除边遍历的方式)
while len(minheap) != 0:
heapq.heappop(minheap)