1. 程式人生 > >頭條面試-後臺研發實習

頭條面試-後臺研發實習

頭條面試的一個特點就是等,面一個小時,等一個小時,從下午五點到中航廣場,晚上將近10點才離開,一共經歷三面。面試中主要是演算法和專案,三個面試官不約而同對簡歷上僅有的一個實驗室專案感興趣(可能是因為這個專案偏研究,簡歷上其他專案都偏工程)。專案相關的東西因人而異,在此不做過多介紹。每次面試有兩道演算法題,寫程式碼主要是看程式碼風格和邏輯嚴密性(比如邊界條件,初始化,終止條件等),然後會讓講思路,不斷優化,過程是討論式的,如果暫時想不到還可以要求提示。

一面

兩道題,第一道題是陣列相關 ,原諒我餓著肚子面試不記得題目了。
第二道是二叉樹((root)尋找兩個節點(p,q)最近公共祖先。之前雖然見過這道題,但沒認真記住,所以寫程式碼實現的時候寫了最容易想到的思路:

  1. 邊界條件:root,p,q不為空,否則直接返回NULL;
  2. 判斷p或者q是否等於root,若等於,則在root左右子樹中查詢另一節點,找到則返回root,否則返回NULL;
  3. 若p和q不等於root,分別在左右子樹查詢p和q,若p和q分別在左右子樹,返回root,否則,遞迴同時包含p和q的子樹。

不過看面試官的反應並不滿意,又讓考慮下思路,這次我簡單描述了一個非遞迴演算法:

先找樹中最左節點,若該節點等於p或者q,則在父節點和父節點的右子樹中查詢另一個節點,若找到則返回當前節點的父節點。
當然這裡有一種特殊情況,如果最左節點有右子樹,則應先在該節點的右子樹中查詢是否滿足條件。

但這個思路雖然較第一個思路優化了不少,應該也不是面試官想要的,最後也沒有想到,一面就到此為止。從面試房間出來在等候區查了一下這個題,各種思路一大堆,沒怎麼認真看,有興趣的多多調研,可以交流下。

二面

第一道是經典的求100萬個數最大的k個數,然後將這個數量級提升到100億中求最大k個數,大概說了下思路,建小頂堆,數量級大的時候先分片再找。
第二題是給定一個有序陣列,然後給定陣列中存在的一個數k,求陣列中與k絕對值之差最小的c個數。稍加分析可以發現,滿足條件的c個數在陣列中一定是連續的,可以用反證法證明,如果不連續,則在缺失的部分可以找到一個數a,其中a與k的絕對值之差必定小於c個數中下標最小的數和下標最大的數中的一個。
有了以上結論,此問題即轉化為尋找c個數中下標最小的數的位置m,從時間複雜度考慮,採用二分查詢即可。記有序陣列為nums,則二分查詢的終止條件為:

|k-nums[m]|<=|nums[m+c]-k| && |k-nums[m-1]|>|nums[m+c]-k|

查詢結束後,下標[m,m+c-1]中的c個數即為所求,當然此處還要考慮數字k的位置,如果k的下標在[m,m+c-1]之間,則應考慮加入nums[m-1]和nums[m+c]中離k最近的一個。

三面

第一個題被問的措手不及,因為是和概率相關,一不留神就想多了,寫了一個自己都看不懂的公式,成功的被面試官證明是錯的。問題描述如下:假設有一個n面的骰子,指定每個面朝上的概率為Pi(i=1,2,3,…,n),要求寫一個類實現初始化以及投擲骰子的函式(返回值為投擲一次朝上的面的數字),額外要求是注意程式碼風格。
這個問題首先是要生成一個隨機數,我考慮的是生成1到n之間的隨機數,然後再隨機,然後成功的暈了。但其實只要生成[0,1]區間的隨機數,如果隨機數落在[sum(P1:Pi-1),sum(P1:Pi)]區間則返回i即可,落在[0,P1]區間返回1。
第二個題是求數列中第k大的數,因為二面的時候遇到的是求k個最大的數,所以首先考慮的也是建立小頂堆,然後遍歷,但時間複雜度為O(n*logk),空間複雜度為O(k),並不是最優的解;再思考了一下,又想到用一個額外的陣列記錄比當前數字大的數字個數,然後再一趟遍歷查詢這個額外陣列中值剛好為k-1的位置對應的數字即為所得,時間複雜度和空間複雜度均為O(n),面試官讓再想想。對比本題和求k個最大數的區別,空間複雜度應該為O(1)才最優,時間複雜度再優化下去應該是O(logn),所以只有二分查詢能滿足,由此想到快排中分治的思想,一趟快排之後前後兩部分分別是比當前數字小和大的數字,所以可以判斷當前數字是第幾大的數,如果不是第k大的,繼續在左邊或者右邊查詢即可。

面完頭條找實習的過程就到此為止啦,終於趕上校招末班車,坐等七月。