堆的产生
堆的产生问题背景:如何在一堆动态变化的数中,以O(1)的复杂度获取最值。
堆的目标:一个数组,支持删除元素和添加元素,保证首元素永远是数堆中的最值。
数组树形化:比如给出数组 , 一层一层依次从左到右加入节点,构造出如下图所示的一颗树,该种树形称为完全二叉树。
对于数组 ,其所有元素的父子关系满足。
完全二叉树:每一层节点都是满的,最底下一层叶子节点从左到右也是满的。
完全二叉树的数组化:可以用数组存储,按层次从左到右遍历树节点。比如下图。
堆的定义:
- 逻辑上是一个完全二叉树,支持删除元素和添加元素,保证根元素永远是数堆中的最值。其所有子树也都是堆。
- 物理存储上,因为完全二叉树的节点可由数组存储,所以是一个数组,所有节点的父/子节点在数组中的位置,可由公式计算得到。以 为例,数组位置从 0 开始计。 的位置是 ,其父节点位置是 ,左子节点下标是 ,右子节点下标是 。
堆示例: 就不是堆,因为 不是最值,且以 为根的子树,根也不是最值。 是堆,树形化后如下图所示:
建堆
建堆的目标:以前面的数组 为例,调整元素位置,使之对应的完全二叉树,符合堆定义。
建堆的思路:把问题分解,从最小的树开始调整。
- 先找到完全二叉树中最低层的父节点。按父子关系公式逆运算,元素下标是 ,就是 。该树只有最多三个节点,父节点与子节点中最大的节点互换位置,如果父节点最大就按兵不动。此时该元素( )为根的子树满足堆定义。
- 继续往前调整节点,此时元素下标是 ,就是 ,父子节点进行同样的比较互换操作 ,如果有更换(与 互换了),那么无法保证新的子节点值 是子树的最大值,所以需要继续将该子节点 和其子节点 进行同样的比较互换操作,一直进行到比较时无需更换,或者没有子节点为止。
- 继续往前发现已经到数组首元素前面了,结束。
- 前面对节点 一路向下的比较调整,称为向下调整。而前面从最低父节点 开始的建堆,我自称为向前建堆。
插入与删除元素
插入和删除的背景: