数据结构 || 关于堆的那些事
-
前面我们说二叉树有两种储存方式
-
1.链式存储(之前已经实现过了)
二叉树的实现(C++版) -
2.顺序存储这便是我们要今天学习的堆了
-
堆:理论上是按照完全二叉树结构存储的 ,物理上却是以数组的方式存储的
堆的概念及结构
- 堆:如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质
- 堆中某个结点的值总是小于/不小于其父节点的值:
- 堆是一颗完全二叉树
- 小根堆特点:根结点的结点值小于其左右孩子的结点值(左右孩子都在的情况下)
- 大根堆特点:根结点的结点值大于左右孩子的结点值(左右孩子存在的情况下)
堆的向下调整
- 当我们拿到一个数组的时,逻辑上将其看成一颗完全二叉树,我们通过从第一个非叶子结点开始向下调整整个数组
此处演示的是小根堆的向下调整 - 大根堆调整成小根堆的过程图示
- 第一步:拿最后一个非叶子结点的值与(其父结点)左右孩子中较小的那个进行交换
- 逻辑上的调整
- 存储结构上的调整(箭头指出的是要调整的两个结点)
- 第二步:从上一个非叶子结点开始调整
- 逻辑上的调整(第一步的调整结果及第二次调整的对象)
- 存储结构上的调整(上一次调整的结果和下一步要调整的两个结点)
- 第三步:对其前面的非叶子结点进行调整
- 逻辑上的调整(第二步调整结果和第三步要调整的结点)
- 可以看到堆中需要调整的结点已经是根结点了,但是调整并没有完全结束,还需要继续调整
- 存储结构上的调整结果(上一次调整的结果和下一步要调整的两个结点)
- 第四步:继续向下调整不符合堆性质的结点
- 逻辑上的调整结果
- 由图可知根结点调整过后,整棵树还是不符合堆的性质,需要继续调整,而此处的调整目标变成了根结点中不符合堆性质的孩子结点,因此我们继续进行调整
- 存储结构上的调整
- 最终调整结果
- 逻辑上的调整结果
- 存储的调整结果
堆的插入
此处我们演示向大根堆中插入数据
-
未插入之前数据已经是有序的了(结点值符合大堆性质),而假如插入的值破坏了这种性质就需要进行向上的调整
-
第一步:在数组尾部插入新的数据,并进行调整
-
第二步:判断插入结点值与其父结点值的大小关系,若符合堆的性质,就结束调整.若不符合堆的性质就交换新结点值和其父结点值.再继续往上调整直到满足堆的性质才能结束调整
堆的删除
删除堆中元素是删除堆顶的数据,将堆顶的数据根最后一个数据交换,然后删除数组最后一个数据,再进行向下调
整算法。
- 交换及删除过程
-
注意: 删除的过程中只是改变了有效访问数目,并没有将空间释放
- 向下调整
- 调整到其符合堆的性质为止