二叉堆是一种特殊的堆,二叉堆是彻底二元树(二叉树)或者是近似彻底二元树(二叉树)。算法
最大堆:父结点的键值老是大于或等于任何一个子节点的键值;
最小堆:父结点的键值老是小于或等于任何一个子节点的键值。数组
插入节点bash
二叉堆的节点插入,插入位置是彻底二叉树的最后一个位置。好比咱们插入一个新节点,值是 0。ui
这时候,咱们让节点0的它的父节点5作比较,若是0小于5,则让新节点“上浮”,和父节点交换位置。spa
继续用节点0和父节点3作比较,若是0小于3,则让新节点继续“上浮”。3d
继续比较,最终让新节点0上浮到了堆顶位置。code
删除节点cdn
二叉堆的节点删除过程和插入过程正好相反,所删除的是处于堆顶的节点。好比咱们删除最小堆的堆顶节点1。blog
这时候,为了维持彻底二叉树的结构,咱们把堆的最后一个节点10补到本来堆顶的位置。排序
接下来咱们让移动到堆顶的节点10和它的左右孩子进行比较,若是左右孩子中最小的一个(显然是节点2)比节点10小,那么让节点10“下沉”。
继续让节点10和它的左右孩子作比较,左右孩子中最小的是节点7,因为10大于7,让节点10继续“下沉”。
这样一来,二叉堆从新获得了调整。
构建二叉堆
构建二叉堆,也就是把一个无序的彻底二叉树调整为二叉堆,本质上就是让全部非叶子节点依次下沉。
咱们举一个无序彻底二叉树的例子:
首先,咱们从最后一个非叶子节点开始,也就是从节点10开始。若是节点10大于它左右孩子中最小的一个,则节点10下沉。
接下来轮到节点3,若是节点3大于它左右孩子中最小的一个,则节点3下沉。
接下来轮到节点1,若是节点1大于它左右孩子中最小的一个,则节点1下沉。事实上节点1小于它的左右孩子,因此不用改变。
接下来轮到节点7,若是节点7大于它左右孩子中最小的一个,则节点7下沉。
节点7继续比较,继续下沉。
这样一来,一颗无序的彻底二叉树就构建成了一个最小堆。
二叉堆通常用数组来表示。若是根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1。所以,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。
如上图所示二叉堆所示用数组方式表示为 | 1 | 5 | 2 | 6 | 7 | 3 | 8 | 9 | 10 |
堆得代码表示
/**
* 上浮调整
*
* @param array 待调整的堆
*/
public static void upAdjust(int[] array) {
int childIndex = array.length - 1;
int parentIndex = (childIndex - 1) / 2;
// temp保存插入的叶子节点值,用于最后的赋值
int temp = array[childIndex];
while (childIndex > 0 && temp < array[parentIndex]) {
//无需真正交换,单向赋值便可
array[childIndex] = array[parentIndex];
childIndex = parentIndex;
parentIndex = (parentIndex - 1) / 2;
}
array[childIndex] = temp;
}
/**
* 下沉调整
*
* @param array 待调整的堆
* @param parentIndex 要下沉的父节点
* @param length 堆的有效大小
*/
public static void downAdjust(int[] array, int parentIndex, int length) {
// temp保存父节点值,用于最后的赋值
int temp = array[parentIndex];
int childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
// 若是有右孩子,且右孩子小于左孩子的值,则定位到右孩子
if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
childIndex++;
}
// 若是父节点小于任何一个孩子的值,直接跳出
if (temp <= array[childIndex])
break;
//无需真正交换,单向赋值便可
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}
/**
* 构建堆
*
* @param array 待调整的堆
*/
public static void buildHeap(int[] array) {
// 从最后一个非叶子节点开始,依次下沉调整
int parent = array.length / 2 - 1;
for (int i = parent; i >= 0; i--) {
downAdjust(array, i, array.length);
}
}
public static void main(String[] args) {
int[] array = new int[]{1, 3, 2, 6, 5, 7, 8, 9, 10, 0};
upAdjust(array);
System.out.println(Arrays.toString(array));
array = new int[]{7, 1, 3, 10, 5, 2, 8, 9, 6};
buildHeap(array);
System.out.println(Arrays.toString(array));
}
[0, 1, 2, 6, 3, 7, 8, 9, 10, 5]
[1, 5, 2, 6, 7, 3, 8, 9, 10]
复制代码
堆排序算法的步骤:
一、把无序数组构建成二叉堆。
二、循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶。
public static void heapSort(int[] array) {
// 1.把无序数组构建成二叉堆。
for (int i = (array.length - 2) / 2; i >= 0; i--) {
downAdjust(array, i, array.length);
}
System.out.println(Arrays.toString(array));
// 2.循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶。
for (int i = array.length - 1; i > 0; i--) {
// 最后一个元素和第一元素进行交换
int temp = array[i];
array[i] = array[0];
array[0] = temp;
// 下沉调整最大堆
downAdjust(array, 0, i);
}
}
复制代码