小史是一個應屆生,雖然學的是電子專業,可是本身業餘時間看了不少互聯網與編程方面的書,一心想進BAT互聯網公司。面試
以前小史在 BAT 三家的面試中已經掛了兩家,今天小史去了 BAT 中的最後一家面試了。算法
簡單的自我介紹後,面試官給了小史一個問題。編程
【面試現場】數組
題目:如何在 10 億數中找出前 1000 大的數?bash
小史:我能夠用分治法,這有點相似快排中 partition 的操做。隨機選一個數 t,而後對整個數組進行 partition ,會獲得兩部分,前一部分的數都大於 t ,後一部分的數都小於 t 。大數據
小史:若是說前一部分總數大於 1000 個,那就繼續在前一部分進行 partition 尋找。若是前一部分的數小於 1000 個,那就在後一部分再進行 partition ,尋找剩下的數。ui
小史:首先,partition 的過程,時間是 o(n)。咱們在進行第一次 partition 的時候須要花費 n ,第二次 partition 的時候,數據量減半了,因此只要花費 n/2,同理第三次的時候只要花費n/4,以此類推。而n + n/2 + n/4 + ...顯然是小於 2n 的,因此這個方法的漸進時間只有 o(n)spa
(注:這裏的時間複雜度計算只是簡化計算版,真正嚴謹的數學證實能夠參考算法導論相關分析。)3d
半分鐘過去了。code
小史一時慌了神。
他回憶起了以前呂老師給他講解 bitmap 時的一些細節。忽然有了一個想法。
小史在紙上畫了畫。
理解了算法以後,小史的代碼寫起來也是很是快,不一下子就寫好了:
/**
* @author xiaoshi on 2018/10/14.
*/
public class TopN {
// 父節點
private int parent(int n) {
return (n - 1) / 2;
}
// 左孩子
private int left(int n) {
return 2 * n + 1;
}
// 右孩子
private int right(int n) {
return 2 * n + 2;
}
// 構建堆
private void buildHeap(int n, int[] data) {
for(int i = 1; i < n; i++) {
int t = i;
// 調整堆
while(t != 0 && data[parent(t)] > data[t]) {
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
}
// 調整data[i]
private void adjust(int i, int n, int[] data) {
if(data[i] <= data[0]) {
return;
}
// 置換堆頂
int temp = data[i];
data[i] = data[0];
data[0] = temp;
// 調整堆頂
int t = 0;
while( (left(t) < n && data[t] > data[left(t)])
|| (right(t) < n && data[t] > data[right(t)]) ) {
if(right(t) < n && data[right(t)] < data[left(t)]) {
// 右孩子更小,置換右孩子
temp = data[t];
data[t] = data[right(t)];
data[right(t)] = temp;
t = right(t);
} else {
// 不然置換左孩子
temp = data[t];
data[t] = data[left(t)];
data[left(t)] = temp;
t = left(t);
}
}
}
// 尋找topN,該方法改變data,將topN排到最前面
public void findTopN(int n, int[] data) {
// 先構建n個數的小頂堆
buildHeap(n, data);
// n日後的數進行調整
for(int i = n; i < data.length; i++) {
adjust(i, n, data);
}
}
// 打印數組
public void print(int[] data) {
for(int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
}
複製代碼
面試官看了一下。
小史熟練地介紹起了本身的項目,因爲準備充分,小史聊起來遊刃有餘。面試官問的幾個問題也進行了詳細的解釋。
小史走後,面試官在系統中寫下了面試評語:
【碰見呂老師】
小史回到學校哼着歌走在校園的路上,正好碰到呂老師。
小史把面試狀況和呂老師說了一下。
小史:感悟還挺深的。雖然平時作過 topN 的問題,知道分治法時間更少。可是碰到具體問題的時候仍是要具體分析,這種大數據量的狀況下反而用堆會更快。
本文做者是個人好朋友欣哥,原文連接以下:
【面試現場】如何在10億數中找出前1000大的數