问题一:一个有序数组(从大到小) 长度为13 中位数为 18
int arr[] = new int[]{1, 3, 5, 6, 11, 14, 18, 21, 27, 29, 31, 56, 59}
但是我们这个数组是动态的,每次都插入一个数据,这样没插入依次中位数就要破环,而且还要每次排序,这样会很慢。
我们思路是这样维护一个大小,顶堆。 大顶堆存放0-5 元素,小顶堆 存放 6到12 ,这样小顶堆的堆顶就是中位数。
为题二:如何求解一个动态数组中,取出排名前k的数据;举一个现实问题,百度文章搜索量如何求动态求排名前10位的文章(文中的搜索量是不断变化的,你买次都遍历求topK是非常耗时间的)
大顶堆:堆顶元素是整个集合中最大的元素,同理 小顶堆是整个集合最小的元素。
这里 我们采用数组形式实现顶锥(另一种实现方式是链表形式)
大顶堆代码:
package com.jxd.test;
public class BigHeap {
private int heapCont[];
/**
* 堆中能存储的最大数据个数
*/
private int tab;
/**
* 堆中已经存在数据的个数
*/
private int count;
public BigHeap(int capacity) {
heapCont = new int[capacity + 1];//因为0号位没法使用
tab = capacity;
count = 0;
}
/**
* n表示原数组中有数据个数
*
* @param heapCont
* @param n
*/
public BigHeap(int[] heapCont, int n) {
this.heapCont = heapCont;
tab = heapCont.length;
count = n;
blindHeap(count);
}
/**
* 向堆中插入数据
* 从下往上法
*/
public boolean insert(int data) {
if (count >= tab) return false;//堆中数据满了
heapCont[++count] = data;//++count 这里注意下,实际是先自增。
int i = count;
int index = 0;
while ((index = i / 2) > 0 && heapCont[i] >= heapCont[index]) {
swap(heapCont, i, index);
i /= 2;
}
return true;
}
/**
* 建堆的过程,把一个原数组建成堆
* n表示数据的个数
* @param n
*/
private void blindHeap(int n) {
for (int i = n / 2; i >= 1; i--) {
heapify(i, n);
}
}
/**
* 获取堆顶元素
*
*/
int getTop() {
if(count==0)return -1;
return heapCont[1];
}
/**
* 删除堆顶
* 从上往下法
*/
public int removeTop() {
if (count == 0) return -1;
int i = 1;
int top=heapCont[i];
heapCont[i] = heapCont[count--];//最后一个元素放到堆顶,然后count-- 相当于删除一个数据
heapify(i, count);
return top;
}
/**
* 堆化
*
* @param i
* @param n
*/
private void heapify(int i, int n) {
int index;
while (true) {
int max = i;
/**
* 实际就是判断以下左子树和右子树谁大。从而决定走哪一个
*/
if ((index = i << 1) <= n && (heapCont[i] < heapCont[index])) //比较左子树
max = index; //i << 1 等于i乘以2
if ((index = (i << 1) + 1) <= n && (heapCont[max] < heapCont[index])) //左子树和右子树比较
max = index;
if (max == i) break;//满足堆的数据结构,父节点大于等于子节点
swap(heapCont, i, max);
i = max;//继续下一次循环
}
}
/**
* 堆的排序 从小到大。
* count表示元素的个数
*/
public void sort() {
int k = count;
while (k > 1) {
swap(heapCont, 1, k);
k--;
heapify(1, k);
}
}
/**
* 数组的互换方法
* 下标source 和target互换
*
* @param a
* @param source
* @param target
*/
private void swap(int a[], int source, int target) {
int sindex = a[source];
int tindex = a[target];
a[source] = tindex;
a[target] = sindex;
}
public static void main(String[] args) {
BigHeap heap = new BigHeap(10);
heap.insert(10);
heap.insert(20);
heap.insert(18);
heap.insert(30);
heap.insert(39);
heap.insert(22);
heap.insert(6);
heap.insert(4);
heap.insert(51);
heap.insert(33);
heap.removeTop();//删除顶
heap.sort();
heap.insert(53);
System.out.println("");
}
}
小顶堆代码: 和大顶堆代码一样,就把代码中的> 改成 <就可以
测试 代码:
package com.jxd.test;
public class testHeap {
public static void main(String[] args) {
int arr[] = new int[]{1, 3, 5, 6, 11, 14, 18, 21, 27, 29, 31, 56, 59};//13
BigHeap bigHeap = new BigHeap(10);
for (int i = 0; i < 6; i++) {
bigHeap.insert(arr[i]);
}
SmallHeap smallHeap = new SmallHeap(10);
for (int i = 6; i < arr.length - 1; i++) {
smallHeap.insert(arr[i]);
}
System.out.println("中位数为" + smallHeap.getTop());
//动态维护数组中的中位数。 我们新添加一个元素 61 {1,3,5,7,6,11,14,18,21,27,29,31,56,59} 14
int node = 7;
if (bigHeap.getTop() >= node) {//进入大顶堆
int top =bigHeap.removeTop();//把小大顶堆堆顶放到小顶堆中
bigHeap.insert(node);//插入新数据
smallHeap.insert(top);
}
else if (smallHeap.getTop() < node) {//进入小顶堆
int top =smallHeap.removeTop();
smallHeap.insert(node);
bigHeap.insert(top);
}
System.out.println("添加元素"+node +"之后的中位数为" + smallHeap.getTop());
}
}
测试结果:
中位数为18
添加元素7之后的中位数为 14
问题二:
package com.jxd.test; public class testHeap { public static void main(String[] args) { int arr[] = new int[]{18, 3, 9, 31, 22, 16, 15, 19, 51, 88, 14, 33, 38};//无序数组 int k = 3; //比如我们排名k的数据 SmallHeap heap = new SmallHeap(k); heap.insert(arr[0]); heap.insert(arr[1]); heap.insert(arr[2]);//先插入前边三个数据 for (int i = 3; i < arr.length; i++) { if(heap.getTop()