1、定义
二叉堆(英语:binary heap)是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树。二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。
— 维基百科
二叉堆分为两种
- 最大堆
最大堆的特点是父节点的键值总是大于等于任一子节点的键值 - 最小堆
最小堆的特点是父节点的键值总是小于等于任一子节点的键值
2、基本操作
二叉堆通常用数组来表示,有两个基本的操作:上浮 和 下沉
- 上浮:添加元素时使用,保持堆的平衡
- 下沉:删除堆定元素时使用,保持堆的平衡
下面展示一个基本的二叉堆(最大堆)
11 / \ 9 10 / \ / \ 5 6 7 8 / \ / \ 1 2 3 4
用数组来表示的话
[11, 9, 10, 5, 6, 7, 8, 1, 2, 3, 4]
其中,如果父节点下标为i的话,左子节点下标可表示为2 * i + 1
,右子节点下标可表示为2 * i + 2
3、上浮(siftUp)
如果要添加一个元素100,100会逐渐上浮,直到到达堆顶
大致流程如下表示:
1、 11 / \ 9 10 / \ / \ 5 6 7 8 / \ / \ / 1 2 3 4 100
2、 11 / \ 9 10 / \ / \ 5 6 100 8 / \ / \ / 1 2 3 4 7
3、 11 / \ 9 100 / \ / \ 5 6 10 8 / \ / \ / 1 2 3 4 7
3、 100 / \ 9 11 / \ / \ 5 6 10 8 / \ / \ / 1 2 3 4 7
java代码
int[] arr;//假设已被创建好
public void siftUp(int index) {
while (index > 0 && arr[parent(index)] < arr[index]) {
int parent = parent(index);
int tmp = arr[parent];
arr[parent] = arr[index];
arr[index] = tmp;
index = parent;
}
}
private int parent(int index) {
return (index - 1) >> 1;
}
4、下沉(siftDown)
以最初的二叉堆为例,假设移出堆顶的11,并将堆尾的4移动到堆顶,接下来就需要下沉来重新形成最大堆
1、 4 / \ 9 10 / \ / \ 5 6 7 8 / \ / 1 2 3
2、 10 / \ 9 4 / \ / \ 5 6 7 8 / \ / 1 2 3
3、 10 / \ 9 8 / \ / \ 5 6 7 4 / \ / 1 2 3
java代码
int[] arr;//假设已创建好
int size;//数组中实际的元素个数
private void siftDown(int index) {
int k = size >> 1;//size是数组实际的元素个数
while (index < k) {
int left = 2 * index + 1;//左子节点
int j = left;
if (left + 1 < size && arr[left + 1] > arr[left])
j = left + 1;//右子节点
if (arr[index] >= arr[j])
break;
int tmp = arr[index];
arr[index] = arr[j];
arr[j] = arr[index];
index = j;
}
}
5、堆排序
- 先将数组构建成一个二叉堆,再将堆顶元素和堆尾元素互换,最后将新堆顶元素下沉,重复此过程,就可以得到一个有序数组
public void heapSort(int[] arr) {
int length = arr.length;
for (int i = (length - 1) / 2; i >= 0; i--) {
siftDown(arr, length, i);
}
for (int i = length - 1; i > 0; i--) {
swap(0, i, arr);
siftDown(arr, i, 0);
}
}
private void siftDown(int[] arr, int length, int index) {
while (2 * index < length) {
int j = 2 * index;
if (j + 1 < length && arr[j + 1] > arr[j])
j = j + 1;
if (arr[index] >= arr[j])
break;
swap(index, j, arr);
index = j;
}
}
private void swap(int i, int j, int[] arr) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}