首先第一个问题:什么是堆?
堆其实是从完全二叉树演变过来的并且用来存储数据的,什么是完全二叉树呢?完全二叉树就是:
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中
在最左边,这就是完全二叉树。我们知道二叉树可以用数组模拟,堆自然也可以。
现在让我们来画一棵完全二叉树:
从图中可以看出,元素的父亲节点数组下标是本身的1/2(只取整数部分),所以我们很容易去模拟,也很容易证明其所有操作都为log级别~~
堆还分为两种类型:大根堆、小根堆
顾名思义,就是保证根节点是所有数据中最大/小,并且尽力让小的节点在上方不过有一点需要注意:堆内的元素并不一定数组下标顺序来排序的!!下标为1就是第一大/小,2是第二大/小……
堆的几个基本操作:
首先堆的上浮过程,
以小根堆为列:说直白一点就是从当前节点与他的父节点进行比较,小于则交换,并且将当前节点的下标更换为它父节点的下标,否则退出。
堆的下浮过程:如果它有子节点,将当前节点与他的左右子节点的最小值进行比较,如果相等则进行交换,并且将当前节点的下标与更换为交换值的子节点的下标。否则退出。
插入操作:每次插入都是从最后一个节点进行插入,并且比较上浮到它自己应有的位置去
弹出操作:弹出操作就是将根节点的元素弹出,这里有一个技巧就是将根元素与尾元素进行交换然后将新的根元素下沉就行了。
堆排序
什么是堆排序?
堆排序:运用堆的性质,我们可以得到一种常用的、稳定的、高效的排序算法————堆排序。堆排序的时间复杂度为O(n*log(n))
,空间复杂度为O(1)
,堆排序的思想是:
对于含有n
个元素的无序数组nums
, 构建一个堆(这里是小顶堆)heap
,然后执行取顶 top得到最小的元素,这样执行n
次得到序列就是排序好的序列。
如果是降序排列则是小顶堆;否则利用大顶堆。
注意到堆的删除操作,如果是删除堆的根节点,则不用考虑执行上浮的操作;若删除的是堆的非根节点,则要视情况决定是pop还是上浮操作,两个操作是互斥的。
- 上浮 shift_up;
- 下沉 shift_down
- 插入 push
- 弹出 pop
- 取顶 top
- 堆排序 heap_sort
case 1 :
删除中间节点i
21,将最后一个节点复制过来;
由于没有进行下沉操作,节点i
的值仍然为6,因此为确保堆的性质,执行上浮
操作;
case 2
删除中间节点i
,将值为11的节点复制过来,执行下沉
操作;
由于执行下沉操作后,节点i
的值不再是11
,因此就不用再执行上浮
操作了,因为堆的性质在下沉
操作生效后已经得到了保持。