小顶堆:
我们利用的特性:每个节点都比左右孩子小
图示:
取数组前n个数,构成小顶堆
然后从数组里面获取数据,如果比堆顶小,直接抛弃,如果比堆顶大,就替换堆顶,并调整堆,使堆始终满足小顶堆的特性
93比18大,替换18
调整堆
然后依次处理下一个数44
对数组内的数调整完成后,前n个大的数 就在小顶堆里面了
平均时间复杂度为Ο(nlogn)
代码实现:
import java.util.Arrays;
public class TopN {
/**
* 父节点
*/
private int parent(int n) {
return (n - 1) / 2;
}
/**
* 左孩子
*/
private int left(int n) {
return 2 * n + 1;
}
/**
* 右孩子
*/
private int right(int n) {
return 2 * n + 2;
}
/**
* 构建堆
*
* @param n
* 构建包含n个节点的堆
* @param data
* 构建堆的数据
*/
public void buildHeap(int n, int[] data) {
for (int i = 1; i < n; i++) {
int t = i;
// 调整堆
while (t != 0 && data[parent(t)] > data[t]) {
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
}
/**
* 调整堆
*
* @param i
* 被调整的数在数据中的下标
* @param n
* 从第n个数开始 不在之前构建的堆里面
* @param data
* 需要被调整的数据
*/
public void adjust(int i, int n, int[] data) {
// 被调整的数小于堆顶 不做任何操作
if (data[i] <= data[0])
return;
// 被调整的数 先和堆顶的数交换
int temp = data[i];
data[i] = data[0];
data[0] = temp;
// 和堆顶交换后的数 和左右子树判断值 再决定和谁交换
int t = 0;
while (left(t) < n && data[left(t)] < data[t] || right(t) < n && data[right(t)] < data[t]) {
// 右孩子比较小,交换
if (right(t) < n && data[right(t)] < data[left(t)]) {
temp = data[t];
data[t] = data[right(t)];
data[right(t)] = temp;
t = right(t);
} else {
// 交换左孩子
temp = data[t];
data[t] = data[left(t)];
data[left(t)] = temp;
t = left(t);
}
}
}
/**
* 找到前n个大的数
*
* @param n
* @param data
*/
public void findTopN(int n, int[] data) {
// 先构建堆
buildHeap(n, data);
// 堆n个数之后的数进行调整
for (int i = n; i < data.length; i++) {
adjust(i, n, data);
}
}
public static void main(String[] args) {
int[] data = { 99, 66, 23, 65, 12, 56, 1, 67, 88, 23, 567, 123, 321, 231, 342, 556, 666, 731, 466, 29 };
System.out.println("构建数组前的数组为:" + Arrays.toString(data));
TopN top = new TopN();
top.buildHeap(7, data);
System.out.println("构建数组后的数组为:" + Arrays.toString(data));
top.findTopN(7, data);
System.out.println("findTopN的数组为:" + Arrays.toString(data));
}
}
测试结果: