(終於等到你)php
寫在前面的一句話:那些年,虐過你的面試題是:_______。面試
今天繼續和你們聊尋找最大的K個數。編程
先來熟悉一下問題:數組
有不少個無序的數(咱們這裏假設爲正整數),並且各不相等,怎麼選出最大的K個數。編程語言
例如:2,5,7,1,3,9,3,6,7,8,5學習
最大的5個數爲:7,9,6,7,8優化
以前咱們給出了四種解法:ui
快速排序和選擇排序,文章詳情:尋找最大的K個數:快排和選擇(一)3d
快排優化和二分搜索,文章詳情:尋找最大的K個數:快排優化和二分搜索(二)排序
今天咱們繼續。開始前,先聊一會。
最終的那個方案,時間複雜度是線性的,也就是說,效率是六個解法中最高的。可是,它有本身的限制。
首先,全部的N個數都是正整數。
其次,它們的取值範圍都不太大。
這裏想說一個什麼問題呢?有所長,必有所短!
老天給你打開了一扇窗,必定會給你關閉一扇門。
老天給你打開了一扇門,必定會給你打開一扇窗。
若是你好,那麼你必定有某些限制;若是你慢,那麼你必定有其它優勢。請相信,只要存在,就是合理。
拿世界上最好的編程語言php來講(沒有之一),隨修改,隨運行,那叫一個6666。可是缺點也是不少,至於什麼,請自行百度。
道理很簡單,若是一個語言,又快,又好,那麼其它編程語言就能夠去死了。如今其它語言都活的好好的。
因此,在什麼狀況下選擇什麼語言,是很是明智的,也是聰明的。不要一味地抱殘守缺!
其實我很不理解一件事,就是面試問那麼多高深的技術,進去以後仍是去寫業務代碼。門檻如此之高,進去之初很容易摔跤。
先來講說最小堆的事。
假如,咱們有一個大小爲K的數組,數組是一個最小堆(數組第一個數爲整個數組最小的值)。請自行百度什麼是最小堆。
咱們從數組S中取一個值,這個值和最小堆的第一個數first對比,有兩種結果:
1,Si <= first 那麼咱們能夠不作任何處理,由於咱們找的是最大的K個數
2,Si > first 這時候,咱們須要將Si的值賦值給最小堆的首元素。這時候咱們可能會破壞最小堆的結構,因此須要重建最小堆。重建後,第一個元素一樣是最小的。就這樣一次比較下去。
不知道看沒看明白,咱們只須要一次將數組S中的元素與最小堆的首元素相比便可。
代碼以下:
package com.xylx.utils.selectK; import com.xylx.utils.GsonUtils; /** * Created by on 17-5-10. */ public class MinHeap { public static void main(String[] args) { int[] arr = {2,6,3,9,4,8}; buildMinHeap(arr); System.out.println(GsonUtils.getJsonFromObject(arr)); change(5, arr); System.out.println(GsonUtils.getJsonFromObject(arr)); change(6, arr); System.out.println(GsonUtils.getJsonFromObject(arr)); } /** * 都建構建最小堆 * @param arr */ public static void buildMinHeap(int[] arr) { for (int i=0; i<arr.length; i++) { handleMinHeap(arr, arr.length, i); //從前向後構建最小堆 } } /** * 檢查value是否比arr[0]大,若是大於,則對堆進行重構 * @param value * @param arr */ public static void change(int value, int[] arr) { if (value > arr[0]) { arr[0] = value; handleMinHeap(arr, arr.length, 0); } } /** * 最小堆排序 * @param arr */ public static void minHeapSort(int[] arr) { for (int i=arr.length-1; i>0; i--) { int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp; handleMinHeap(arr, i, 0); } } /** * 節點轉換 * @param arr * @param heapSize * @param current */ public static void handleMinHeap(int[] arr, int heapSize, int current) { int leftChildIndex = getLeftChiledIndex(current); int rightChildIndex = getRightChildIndex(current); int minIndex = current; //找到三個節點中最小的那個 if (leftChildIndex<heapSize && arr[current] > arr[leftChildIndex]) { minIndex = leftChildIndex; } if (rightChildIndex<heapSize && arr[minIndex] > arr[rightChildIndex]) { minIndex = rightChildIndex; } if (minIndex != current) { int tmp = arr[current]; //將最小的值轉移到父節點位置,這樣可能會破壞原先子節點的結果,因此須要遞歸 arr[current] = arr[minIndex]; arr[minIndex] = tmp; handleMinHeap(arr, heapSize, minIndex); } } /** * 獲取左孩子節點位置 * @param current * @return */ public static int getLeftChiledIndex(int current) { return (current<<1) + 1; } /** * 獲取右孩子節點位置 * @param current * @return */ public static int getRightChildIndex(int current) { return (current<<1) + 2; } /** * 獲取父節點位置 * @param current * @return */ public static int getParentIndex(int current) { return (current-1)>>1; } }
說完了上面那個,讓咱們看看最終的大boss。
下面這個是面試中大部分面試官想要的結果。它的工做流程是:
獲得數組中最大的值MAX,這樣數組中全部的數都在[0, MAX]之間。申請一個數組countArr,大小爲MAX+1。countArr每條記錄是這個下標所表明的數組在數組中出現的次數。這樣,咱們只須要從前向後找到前K個最大的數便可。
代碼以下:
package com.xylx.utils.selectK; /** * Created by zongjh on 17-5-8. */ public class FinalSelectK { public static void main(String[] args) { int[] arr = Constans.getLengthArr(100); Constans.printArr(arr); selectK(arr); } public static void selectK(int[] arr) { int max = getMax(arr); System.out.println("max="+max); int[] countArr = new int[max+1]; initArr(countArr); for (int i=0; i<arr.length; i++) { int index = arr[i]; countArr[index] = countArr[index] + 1; } int num = getK(countArr, max); System.out.println("num="+num); printK(arr, num); } private static void printK(int[] arr, int min) { int index = 0; System.out.println("最大的K個數:"); for (int i=0; i<arr.length; i++) { if (arr[i] > min) { System.out.print(arr[i]+" "); index++; } } for (int i=0; i<(Constans.K-index); i++) { System.out.print(min+" "); } } /** * 尋找第K大的數 * @param countArr * @param max * @return */ public static int getK(int[] countArr, int max) { int num = 0; int sumCount = 0; for (int i=max-1; i>0; i--) { sumCount += countArr[i]; if (sumCount >= Constans.K) { num = i; break; } } return num; } public static void initArr(int[] arr) { for (int i=0; i<arr.length; i++) { arr[i] = 0; } } /** * 獲得數組最大值 * @param arr * @return */ public static int getMax(int[] arr) { if (arr == null || arr.length < 1) { return Integer.MIN_VALUE; } int max = Integer.MIN_VALUE; for (int i=0; i<arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } }
這裏的輔助類,請參考以前的文章,看開頭。
終於寫完了,這個尋找最大的K個數。
最後一句話:說不定哪天你就用到這個面試題了!
其餘技術文章:
喜歡聊技術或者聊觀點,能夠加入公號:【花生讀書匯】
一塊兒:勵志,成長,學習,分享。