1. 問題背景
在實際應用中,我們經常會遇到在一大推資料中找出最大的幾個數的問題,也就是我們提到的TOP K問題。K表示需要找出資料的數量 2. 解決方案
TOP K問題也有多種解決方案,比如排序,最後截取靠前或者靠後的K個資料。當資料量小的時候,排序解決起來當然可以,演算法簡單,排序演算法也有很多現成的。當資料量很大時,維護一個很長的數組,不管是空間儲存上還是排序耗費的時間上都可能難以接受。這時我們可以採用最小堆的方式來實現,只需要維護一個K長度的數組就行 3. 具體實現
資料結構最小堆MinHeap, 其中最重要的是堆的調整adjustHeap
package org.cyxl.common;/** * Created by jeff on 16/5/11. */public class MinHeap { // 堆的儲存結構 - 數組 private int[] data; /** * 初始化堆中的資料,以int的最小值作為初始值 * * @param k */ public MinHeap(int k) { this.data = new int[k]; for(int i=0;i<k;i++){ data[i]=Integer.MIN_VALUE; } } private void adjustHeap(int i) { //擷取左右結點的數組下標 int l = left(i); int r = right(i); // 這是一個臨時變數,表示 跟結點、左結點、右結點中最小的值的結點的下標 int min = i; // 存在左結點,且左結點的值小於根結點的值 if (l < data.length && data[l]<data[i]){ min = l; } // 存在右結點,且右結點的值小於以上比較的較小值 if (r < data.length && data[r]<data[min]){ min = r; } // 左右結點的值都大於根節點,直接return,不做任何操作 if (i == min) return; // 交換根節點和左右結點中最小的那個值,把根節點的值替換下去 swap(i, min); // 由於替換後左右子樹會被影響,所以要對受影響的子樹再進行adjustHeap adjustHeap(min); } /** * 擷取右結點的數組下標 * @param i * @return */ private int right(int i) { return (i + 1) << 1; } /** * 擷取左結點的數組下標 * @param i * @return */ private int left(int i) { return ((i + 1) << 1) - 1; } /** * 交換元素位置 * * @param i * @param j */ private void swap(int i, int j) { int tmp = data[i]; data[i] = data[j]; data[j] = tmp; } /** * 將數值加入到堆中 * * @param element */ public void add(int element) { if(element>data[0]) { data[0] = element; adjustHeap(0); } } public int[] getData(){ return data; } @Override public String toString(){ StringBuilder builder = new StringBuilder(); builder.append("{"); for (int i=0;i<data.length;i++){ builder.append(data[i]); if(i!=data.length-1){ builder.append(","); } } builder.append("}"); return builder.toString(); }}4. 測試
public static void main(String[] args){ int[] a = {12,1,5425,12,43,734,343,999,8,99,55,2,222,444,11}; //求最大的10個 int k=10; MinHeap heap = new MinHeap(k); for(int i:a){ heap.add(i); } System.out.print(heap); }
輸出結果是
{12,43,55,99,222,343,999,734,5425,444}5. 擴充
5.1 當然實際應用中,我們找的可能不是單純的數字。更可能是和這些數字相關的某些對象。比如找出年齡最大的10個使用者,這時我們的User對象可以實現Comparable介面來實現比較。
5.2 這裡我們是最小堆來實現TOP K問題,當然也可以用最大堆來找出最小的K個值
5.3 對於最小(大)堆的定義以及特點可以網上查看一下