海量資料處理問題(Top k問題)的實現
阿新 • • 發佈:2019-02-19
在很多網際網路公司的面試題中,都可能會問到海量資料處理的題目,比如在幾千億個資料中如何獲取10000個最大的數?這其實就是一個Top k問題,如何從億萬級的資料中得到前K個最大或者最小的數字。
一個複雜度比較低的演算法就是利用最小堆演算法,它的思想就是:先建立一個容量為K的最小堆,然後遍歷這幾千億個數,如果對於遍歷到的數大於最小堆的根節點,那麼這個數入堆,並且調整最小堆的結構,遍歷完成以後,最小堆的數字就是這幾千億個數中最大的K個數了。
先來介紹一下最小堆:最小堆(小根堆)是一種資料結構,它首先是一顆完全二叉樹,並且,它所有父節點的值小於或等於兩個子節點的值。最小堆的儲存結構(物理結構)實際上是一個陣列 。
因為它是一個完全二叉樹,對於下標小於 陣列.length/2 - 1 時有葉子節點 , 對於下標為i(基0),其左節點下標為2i + 1,右節點下標為2i + 2。
最小堆如圖所示,對於每個非葉子節點的數值,一定不大於孩子節點的數值。這樣可用含有K個節點的最小堆來儲存K個目前的最大值(當然根節點是其中的最小數值)。
每次有資料輸入的時候可以先與根節點比較。若不大於根節點,則捨棄;否則用新數值替換根節點數值。並進行最小堆的調整。
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); } } }