1. 堆的概念与特征
堆是将一组数据按照完全二叉树的存储顺序,将数据存储在一维数组中的结构,对的结构有两种,一种称为大顶堆,一种称为小顶堆。
- 小顶堆:任意节点的值均小于等于它的左右孩子,并且最小的值位于堆顶,即根节点处
- 大顶堆:任意节点的值均大于等于它的左右孩子,并且最大的值位于堆顶,即根节点处
也可称为大根堆,小根堆,或者最大堆,最小堆,
假设一个节点的下标为i。
- 当i = 0时,为根节点。
- 当i >= 1时,父节点为(i - 1) / 2。
有些地方叫做堆,有些地方叫做队列,两者到底是什么关系?
优先队列:就是一种队列,它的工作是poll()/peek()出队列中最大、最小的那个元素,所以叫带有优先级的队列。能够实现优先功能的队列不一定只有堆,例如二项堆、平衡树、线段树、c++会用二进制分组的vector来实现一个优先队列。
堆:堆是一个很大的概念,他并不一定是完全二叉树,用完全二叉树是因为这个容易被数组存储,但除了二叉堆之外,我们还有二项堆,斐波那契堆,这种不属于二叉树。
2. 对的构造过程
使用数组构建堆,按照层次将所有元素依次填入二叉树中,使其成为二叉树,然后不断调整,使其最终符合堆结构。
将元素依次排列到完全二叉树上去,如下图所示。
- int i = (size - 2) / 2 = 4(思考一下这里为什么是size-2而不是size -1)。找到数组中的4号下标。65大于其孩子,满足最大堆性质,不用交换。
- 然后 i = i - 1;用2和其孩子比较,2和204交换。交换之后满足最大堆,如下图左
- 54和其孩子比较,54和92交换,92所在子树满足最大堆,如下图右。
- 23和其孩子比较,23和204交换,交换完之后,23的子树却不满足了,还需要调整其子树,如下两图
- 12和204交换,仍然出现不平衡情况,继续调整子树。直到根节点满足要求。
这样就建成了一个大顶堆,根元素是最大的那个
3. 插入操作
插入300,将其按顺序插入到31的右孩子位置,然后不断向上爬,31<300,所以两者交换,再向上发现300比65大,交换,300>204,交换,最后就是最新堆。
4. 删除操作
堆中的数据特殊,对堆中数据进行的操作都是针对堆顶的元素,即每次从堆顶获取的最大值或最小值,其他的不关心,所以删除的时候,也是删除堆顶。删除堆顶整个结构会被破坏,需要再次调整。如果直接删除堆顶,群龙无首就不易管理了。实际策略是将队中的最后 一个元素与根元素交换,然后再删除堆的最后一个元素。之后再交换调最大堆,直到堆顶元素满足最大堆性质。
交换完成之后
关于堆的使用口诀
查找:找大用小,大的进;找小用大,小的进。
排序:升序用小,降序用大。