题目地址:
https://www.lintcode.com/problem/heapify/description
将一个数组堆化,就地变为最小二叉堆。
先设置一个percolateDown的函数,它的作用是对根节点为 的二叉堆进行下滤操作。然后从整个二叉堆最后一个有孩子的节点到堆顶依次进行下滤操作即可。代码如下
public class Solution {
/*
* @param A: Given an integer array
* @return: nothing
*/
public void heapify(int[] A) {
// write your code here
// A中最后一个有孩子的元素下标是(A.length - 1) >> 1
for (int i = (A.length - 1) >> 1; i >= 0; i--) {
percolateDown(A, A.length, i);
}
}
private void percolateDown(int[] A, int n, int i) {
// 为判断A[i]是否破坏堆的性质,我们需要找到A[i]以及它的两个孩子三者中最小值的下标
// 如果i有孩子,就进入循环
while ((i << 1) + 1 < n) {
// 先假设左孩子是三者中最小值的下标
int child = (i << 1) + 1;
// 如果存在右孩子,且右孩子比左孩子还小,就把child更新为右孩子
if ((i << 1) + 2 < n && A[child] > A[(i << 1) + 2]) {
child = (i << 1) + 2;
}
// 如果A[i]已经是三者中最小值了,就不用下滤了,退出循环
if (A[i] <= A[child]) {
break;
}
// 否则把A[i]换下去
swap(A, i, child);
// 从下一层开始继续下滤
i = child;
}
}
private void swap(int[] A, int i, int j) {
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
时间复杂度 ,空间 。
时间复杂度证明:每一层在下滤中交换的次数最多是其高度(高度是指它所在的深度减去叶子层的深度),所以可知: 所以时间复杂度 。
算法正确性证明:只需证明对于两个最小二叉堆,如果把它们接在一个树根下面分别作为左右孩子时,只需要对树根做下滤操作就可以使得整个二叉树满足堆性质(由算法的实现,不需要考虑complete的问题)。数学归纳法。如果两个堆的节点和等于 ,结论显然成立。假设对于左右堆节点和小于 的时候结论成立。那么从树根开始下滤的时候,第一次下滤已经可保证一边的堆已经不需要调整了,而新的要调整的一边的树根下面接着两个节点数严格小于 的堆,由数学归纳法,结论成立。