topk問題是指從大量資料(源資料)中獲取最大(或最小)的k個資料。
對於這個問題,解決方法有很多:
方法一:對源資料中所有資料進行排序,取出前k個資料,就是topk。
但是當資料量很大時,只需要k個最大的數,整體排序很耗時,效率不高。
方法二:維護乙個k長度的陣列a,先讀取源資料中的前k個放入陣列,對該陣列進行公升序排序,再依次讀取源資料第k個以後的資料,和陣列中最小的元素(a[0])比較,如果小於a[0]直接pass,大於的話,就丟棄最小的元素a[0],利用二分法找到其位置,然後該位置前的陣列元素整體向前移位,直到源資料讀取結束。
這比方法一效率會有很大的提高,但是當k的值較大的時候,長度為k的資料整體移位,也是非常耗時的。
對於這種問題,效率比較高的解決方法是使用最小堆。
最小堆(小根堆)是一種資料結構,它首先是一顆完全二叉樹,並且,它所有父節點的值小於或等於兩個子節點的值。
最小堆的儲存結構(物理結構)實際上是乙個陣列。如下圖:
堆有幾個重要操作:
buildheap:將普通陣列轉換成堆,轉換完成後,陣列就符合堆的特性:所有父節點的值小於或等於兩個子節點的值。
heapify(int i):當元素i的左右子樹都是小根堆時,通過heapify讓i元素下降到適當的位置,以符合堆的性質。
回到上面的取topk問題上,用最小堆的解決方法就是:先去源資料中的k個元素放到乙個長度為k的陣列中去,再把陣列轉換成最小堆。再依次取源資料中的k個之後的資料和堆的根節點(陣列的第乙個元素)比較,根據最小堆的性質,根節點一定是堆中最小的元素,如果小於它,則直接pass,大於的話,就替換掉跟元素,並對根元素進行heapify,直到源資料遍歷結束。
最小堆的實現:
public class minheap
// 將陣列轉換成最小堆
private void buildheap()
}private void heapify(int i)
// 獲取右結點的陣列下標
private int right(int i)
// 獲取左結點的陣列下標
private int left(int i)
// 交換元素位置
private void swap(int i, int j)
// 獲取對中的最小的元素,根元素
public int getroot()
// 替換根元素,並重新heapify
public void setroot(int root)
}
利用最小堆獲取topk:
public class topk;
// 獲取top5
int top5 = topk(data, 5);
for(int i=0;i<5;i++)
}// 從data陣列中獲取最大的k個數
private static int topk(int data,int k)
// 轉換成最小堆
minheap heap = new minheap(topk);
// 從k開始,遍歷data
for(int i= k;iroot)
}return topk;
}}
最大堆 最小堆Java實現,解決TOP K問題
最大堆,最小堆類似,以下以最小堆為例進行講解。最小堆是滿足以下條件的資料結構 它是一棵完全二叉樹 所有父節點的值小於或等於兩個子節點的值 除了最後一層之外的其他每一層都被完全填充,並且所有結點都保持向左對齊。對於topk問題,解決方法有很多 方法一 對源資料中所有資料進行排序,取出前k個資料,就是t...
最大堆 最小堆 解決TOPK問題
堆 實質是一顆完全二叉樹,最大堆的特點 父節點值均大於子節點 最小堆的父節點值均小於子節點 一般使用連續記憶體儲存堆內的值,因而可以根據當前節點的索引值推斷子節點的索引值 節點i的父節點為 i 1 2 節點j的左子結點 j 2 1 節點j的右子結點 j 2 2 以下 實現了最大堆最小堆,當比較函式使...
java解決topk問題
面試題中經常用到堆,這裡總結一下。方法一 對源資料中所有資料進行排序,取出前k個資料,就是topk。但是當資料量很大時,只需要k個最大的數,整體排序很耗時,效率不高。方法二 維護乙個k長度的陣列a,先讀取源資料中的前k個放入陣列,對該陣列進行公升序排序,再依次讀取源資料第k個以後的資料,和陣列中最小...