TopK問題的描述:
指定n個數字,找出其中最大的k個數,這就是經典的TopK問題
解決方法一:全局排序
- 將n個數進行全排序,取出最大的k個,即是所需的結果
- 代碼:
public int[] topK(int[] array, int k) { Arrays.sort(array); return Arrays.copyOfRange(array, array.length - k, array.length); }
- 時間複雜度是O(N*logN)
解決方法二:局部排序
- 其實沒有必要將所有的元素都排序,只需要將前k個最大的值排序出來就可以停止排序,得到的k個值就是需要的結果
- 代碼
public int[] topK(int[] array, int k) { for(int i = 0; i < k; i++) { for(int j = array.length - 1; j > 0; j--) { if(array[j] > array[j - 1]) { int tmp = array[j]; array[j] = array[j - 1]; array[j - 1] = tmp; } } } }
- 時間複雜度是O(k*N)
解決方法三:堆
- 構建一個k大小的小堆,先將前k個元素放入堆中,然後遍歷剩下的元素,如果大於堆頂的元素,就和堆頂的元素進行交換,遍歷結束後,得到的堆上的值就是前k個最大的值
- 代碼
public Integer[] topK(int[] array, int k) { PriorityQueue<Integer> queue = new PriorityQueue<>(); for(int i = 0; i < k; i++) { queue.add(array[i]); } for(int i = k; i < array.length; i++) { if(array[i] > queue.peek()) { queue.poll(); queue.add(array[i]); } } return (Integer[])queue.toArray(); }
- 時間複雜度:O(N*logK)
解決方法四:隨機選擇
- 使用減治的的思想,制定一個元素flag將比flag大的元素放在他左邊,比他小的放在他右邊
- 如果flag的的下標index比k大說明前k個大的元素都在flag左邊的區間,然後在他左區間內重複第一步直到找到下標爲k的值
- 如果flag的下標index比k小說明只要在他的右區間內重複第一步找到下標爲k-index的值
- 找到第k個大的值後再進行此一步驟,它左邊的所有的元素就是前k個最大的值
-
代碼:
public int[] topK5(int[] array, int k) { int left = 0; int right = array.length - 1; //因爲數組下標是以0開始的,因此第k個的小標爲k - 1,因此傳入的爲k - 1 int flag = RS(array, left, right, k - 1); //返回值flag爲第k個最大值的下標,因此需要前k個最大的值時,拷貝數組的範圍是[0, flag + 1) return Arrays.copyOfRange(array, 0, flag + 1)); } private int RS(int[] array, int left, int right, int k) { if (left >= right) { return left; } int index = partition(array, left, right); int temp = index - left; if(temp >= k) { return RS(array, left, index - 1, k); } else { return RS(array, index + 1, right, k - index); } } private int partition(int[] array, int left, int right) { int tmp = array[left]; int l = left; int r = right; while(l < r) { while(l < r && array[r] <= tmp) { r--; } array[l] = array[r]; while(l < r && array[l] >= tmp) { l++; } array[r] = array[l]; } array[l] = tmp; return l; }
- 時間複雜度:O(N)