問題一:一個有序數組(從大到小) 長度爲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()<arr[i]){//如果數據大於堆頂 heap.removeTop(); heap.insert(arr[i]); } } System.out.println("排名前"+k+"數據" +bigHeap.getTop());//這裏你斷點調試下 都在SmallHeap 數組中 //動態數據 比如我們添加一個數據 位99 我們先不着急添加到數組中,先在小頂堆中看看是否大於小頂堆的 堆頂元素 int node =99; if(heap.getTop()<node){ heap.removeTop();//刪除堆頂 然後添加這個新元素 heap.insert(node); } int arrNew[] = new int[]{18, 3, 9, 31, 22, 16, 15, 19, 51, 88, 14, 33, 38,99};//無序數組 System.out.println("動態排名前"+k+"數據"+heap.getTop() );//這裏你斷點調試下 都在SmallHeap 數組中 } }