1. 程式人生 > >海量資料處理問題(Top k問題)的實現

海量資料處理問題(Top k問題)的實現

 在很多網際網路公司的面試題中,都可能會問到海量資料處理的題目,比如在幾千億個資料中如何獲取10000個最大的數?這其實就是一個Top k問題,如何從億萬級的資料中得到前K個最大或者最小的數字。

    一個複雜度比較低的演算法就是利用最小堆演算法,它的思想就是:先建立一個容量為K的最小堆,然後遍歷這幾千億個數,如果對於遍歷到的數大於最小堆的根節點,那麼這個數入堆,並且調整最小堆的結構,遍歷完成以後,最小堆的數字就是這幾千億個數中最大的K個數了。

    先來介紹一下最小堆:最小堆(小根堆)是一種資料結構,它首先是一顆完全二叉樹,並且,它所有父節點的值小於或等於兩個子節點的值。最小堆的儲存結構(物理結構)實際上是一個陣列


    因為它是一個完全二叉樹,對於下標小於 陣列.length/2 - 1 時有葉子節點 , 對於下標為i(基0),其左節點下標為2i + 1,右節點下標為2i + 2。

    最小堆如圖所示,對於每個非葉子節點的數值,一定不大於孩子節點的數值。這樣可用含有K個節點的最小堆來儲存K個目前的最大值(當然根節點是其中的最小數值)。

    每次有資料輸入的時候可以先與根節點比較。若不大於根節點,則捨棄;否則用新數值替換根節點數值。並進行最小堆的調整。

    下面就把java程式碼的實現貼上來把,建立堆的複雜度是O(N),調整最小堆的時間複雜度為O(logK),因此Top K演算法(問題)時間複雜度為O(NlogK).
class TopK {
    //建立堆
    int[] createHeap(int a[], int k) {
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = a[i];
        }
        //完全二叉樹的陣列表示中,下標小於等於result.length / 2 - 1才有子節點
        for (int i = result.length / 2 - 1;i >= 0;i--){
            heapify(i,result);
        }
        return result;
    }

    void heapify(int i,int[] result){
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        int smallest = i;
        if (left < result.length && result[left] < result[i]){
            smallest = left;
        }
        if (right < result.length && result[right] < result[smallest]){
            smallest = right;
        }
        if (smallest == i){
            return;
        }
        else {
            int temp = result[i];
            result[i] = result[smallest];
            result[smallest] = temp;
        }
        heapify(smallest,result);
    }

    //調整堆
    void filterDown(int a[], int value) {
        a[0] = value;
        int parent = 0;

        while(parent < a.length){
            int left = 2*parent+1;
            int right = 2*parent+2;
            int smallest = parent;
            if(left < a.length && a[parent] > a[left]){
                smallest = left;
            }
            if(right < a.length && a[smallest] > a[right]){
                smallest = right;
            }
            if(smallest == parent){
                break;
            }else{
                int temp = a[parent];
                a[parent] = a[smallest];
                a[smallest] = temp;
                parent = smallest;
            }
        }
    }

     //遍歷陣列,並且調整堆
    int[] findTopKByHeap(int input[], int k) {
        int heap[] = this.createHeap(input, k);
        for(int i=k;i<input.length;i++){
            if(input[i]>heap[0]){
                this.filterDown(heap, input[i]);
            }

        }
        return heap;

    }

    public static void main(String[] args) {
        int a[] = { 100,101,5,4,88,89,845,45,8,4,5,8,452,1,5,8,4,5,8,4,588,44444,88888,777777,100000};
        int result[] = new TopK().findTopKByHeap(a, 5);
        for (int temp : result) {
            System.out.println(temp);
        }
    }
}