1. 程式人生 > >【面經】猿題庫-2017年8月25日,散招實習生

【面經】猿題庫-2017年8月25日,散招實習生

首先感謝熱心助人的崔同學,耐心給我講解猿題庫的面試風格,讓我能安心只准備了演算法和system design。不過演算法也沒準備,最近正常刷題而已;system design也只是複習了下搜狗的專案。相當於是裸面了。。。萬幸其他方面一點都沒有問,最後也拿到了實習offer。

一面

一面的面試官看起來不到30,應該是普通研發,當然很可能是準mentor。進屋介紹了下面試流程和公司,然後讓我問問題。我覺得過不過都沒準呢有什麼好問的,所以接話茬問了幾個面試流程相關的問題。

演算法

都說猿題庫主要考演算法,我以為是暴力的上來就寫題,這面試官不錯,先讓我自我介紹放鬆了一下,然後就說,“我們寫個題吧”。

連結串列,交換兩元素

給定一個單鏈表的頭結點head,兩個值v1、v2,在連結串列中找到這兩個結點並交換。

跟面試官確定的資訊:

  • 有無prev
  • Node的結構(val,next)
  • 返回值

題目很簡單,那很可能在考察邊界條件和coding style;演算法方面cc大神說的好,連結串列靠畫圖;交換陣列需要記錄前置節點,給頭結點增加前置節點dummyNode,簡化程式碼,也簡化邊界條件。

剛要寫,面試官就攔住我,跟我說可以先聊聊思路,能先動嘴皮我萬分感謝啊,,,“邊界條件先不管,演算法的主要過程是……”。面試官肯定之後才讓我寫程式碼。

演算法很簡單,直接看程式碼吧:

// define of Node(val, next)
public Node swap(Node head, int v1, int v2) {
  // no need to swap
  if (head == null || head.next == null) {
    return head;
  }
  // no need to swap
  if (v1 == v2) {
    return head;
  }

  Node dummy = new Node(0, head);
  Node prev1 = dummy;
  Node prev2 = dummy;
  Node prev = dummy;
  while
(prev.next != null) { if (prev.next.val == v1) { prev1 = prev; } if (prev.next.val == v2) { prev2 = prev; } } // no need to swap if (prev1 == dummmy || prev2 == dummmy) { return head; } internalSwap(prev1, prev2); return dummy.next; } private void internalSwap(Node prev1, Node prev2) { if (prev1.next == prev2) { internalSwapNextTwo(prev1); return; } if (prev2.next == prev1) { internalSwapNextTwo(prev2); return; } Node next1 = prev1.next; Node next2 = prev2.next; prev1.next = next2; prev2.next = next1; Node tmp = next1.next; next1.next = next2.next; next2.next = tmp; return; } private void internalSwapNextTwo(Node first) { Node second = first.next; Node third = second.next; second.next = third.next; third.next = second; first.next = third; return; }

寫完程式碼,面試也是先讓我拿著程式碼講講思路。這部分主要是免去面試官硬讀程式碼的麻煩,也方便麵試官考察你程式碼的模組性,甚至一些變數、函式的命名。演算法主體剛才講過,於是我一句話帶過,重點講了swap方法中的邊界條件,然後面試官就開始自己看程式碼。之後又讓我講internalSwap方法的原理,我也是先講演算法主體,再講邊界條件。

矩陣旋轉

面試官說這個題很常見,但是我只聽說過,,,後悔自己當時沒看。

給定一個n*n的矩陣,元素為整數,將矩陣旋轉。

跟面試官確定的資訊:

  • 是按照旋轉後的順序輸出矩陣元素即可,還是要改變原來的矩陣
  • 返回值

我只知道有矩陣旋轉這道題,所以剛聽完題目的我是懵逼的。那沒辦法,只能硬著頭皮上。

這道題我開始沒溝通好,沒有問返回值,以為只要按照旋轉後的順序輸出矩陣元素即可(控制迴圈順序)。說完思路,面試官意識到我理解錯了題意,耐心告訴我要返回一個矩陣。但也沒有急著否定我的方法,而是問我這種方法的時間複雜度和空間複雜度。那就都是O(n^2)了,面試官就說也可以改變原來的矩陣,降低空間複雜度。

我想了一會,想到reverse操作一般是O(1)的,而且經常能通過各種reverse的組合達到神奇的效果。於是就想著先按照斜主對角線reverse一下,也就是斜翻——還差點;再按照豎中軸線reverse一下,也就是對翻,握草正好搞定。

我用3*3的矩陣跟面試官說了思路後,面試官又讓我實驗4*4的矩陣,,,我以為有問題,結果畫完發現是正確的。面試官就說,“恩,實驗了也對吧。這其實是個數學問題,跟矩陣的性質有關,你能不能證明一下?”我大學線代課都是睡過去的,跟面試管坦誠線代真的忘光了,於是就直接讓我寫程式碼了。

程式碼:

public int[][] transfer(int[][] matrix) {
  if (matrix == null || matrix.length <= 1) {
    return matrix;
  }
  if (matrix[0] == null || matrix[0].length <= 1) {
    return matrix;
  }

  xiefan(matrix);
  duifan(matrix);
  return matrix;
}

private void xiefan(int[][] matrix) {
  for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < i; j++) {
      swap(matrix, i, j, j, i);
    }
  }
}

private void duifan(int[][] matrix) {
  for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[0].length / 2; j++) {
      swap(matrix, i, j, i, matrix[0].length - 1 - j);
    }
  }
}

private swap(int[][] matrix, int x1, int y1, int x2, int y2) {
  int tmp = matrix[x1][y1];
  matrix[x1][y1] = matrix[x2][y2];
  matrix[x2][y2] = matrix[x1][y1];
}

後面的過程跟上一題一樣,分析時間複雜度與空間複雜度等。

網上更流行的解法是直接旋轉一圈,我面試時也想到了,不過覺得不好推導,沒有reverse的性質通用。不過我覺得需要掌握這種用法,也不用硬記,看過一遍key point,面試時很快能推出來。參考LeetCode:Rotate Image的解法2。

簡單聊專案

兩道演算法題之後,時間就差不多了,面試官讓我介紹下我在搜狗最滿意的一個專案,我回答的非常亂,還好這次主要不是面試system design。然後面試官讓我等一下二面就走了,我趕緊複習之前整理的專案介紹。

二面

二面的面試官,,,確實很帥,讓我想起了去年9月份在nice的面試,不過面到一半去開會了,讓我等20分鐘,,,結果我等了一個小時。中間還有更尷尬的事情,不過這不重要,也不是人家故意的,面試最重要。

本來是先考system design,再考演算法,不過system design考到一半面試官就去開會了,丟給我一道演算法題,開完會也是先講的演算法題,所以這裡先介紹演算法部分。

演算法

非遞迴版歸併排序

只考了一道演算法題,而且面試官說完題意,看我沒有思路就先走了,留給我20分鐘思考+程式碼。

知道歸併排序吧?歸併排序一般用遞迴實現,如果不用遞迴怎麼實現呢?

跟面試官確定的資訊:

  • 返回值

對,才開始刷題的我也沒見過這道題。但是過了一會也想通了,就是用迴圈模擬遞迴版歸併排序的執行過程,演算法描述如下:

  1. 申請陣列B,大小為A.length
  2. 設segLen為1,定義長度為segLen的子陣列為1個seg
  3. 每兩個seg為一組,分別做2路歸併
  4. 將陣列B拷貝回A
  5. segLen = segLen * 2
  6. 如果segLen小於A.length,則回到步驟3;否則繼續
  7. 返回陣列A

因為面試官出去開會了,所以我直接寫了程式碼:

public int[] mergeSort(int[] array) {
  if (array == null || array.length <= 1) {
    return array;
  }

  int[] arrA = array;
  int[] arrB = new int[arrA.length];
  for (int seg = 1; seg < arrA.length; seg *= 2) {
    for (int i = 0; (i + 1) * seg < arrA.length; i++) {
      merge(arrA, i * seg, (i + 1) * seg, seg, arrB);
    }
    System.arrayCopy(arrB, arrA);
  }

  return arrA;
}

private void merge(int[] arrA, int start1, int start2, int seg,
              int[] arrB){
  int l = start1;
  int r = start2;
  int i = start1;
  while (l < start1 + seg && r < start2 + seg
         && l < arrA.length && r < arrA.length) (
    if (arrA[l] <= arrA[r]) {
      arrB[i] = arrA[l];
      l++;
    } else {
      arrB[i] = arrA[r];
      r++;
    }
    i++;
  )
  while (l < start1 + seg && l < arrA.length) {
    arrB[i] = arrA[l];
    l++;
    i++;
  }
  while (r < start2 + seg && r < arrA.length) {
    arrB[i] = arrA[r];
    r++;
    i++;
  }
  return;
}

後面也是分析時間複雜度和空間複雜度等。注意如何分析這道題的時間複雜度:外層迴圈O(logn),內層迴圈O(n),陣列拷貝O(n),所以演算法整體是O(nlogn)。

system design

背景不表,精簡描述如下:

學生每天都會做題,要求設計一個架構,學生做題後,能實時看到自己的排名、前100名的排行榜,作業量每天更新。

跟面試官確定的資訊:

  • 排行榜按照什麼排序——作業量
  • 作業量是計算作業次數還是總量——總量

有一些system design的題目很經典,在面試中經常出現。我不知道這種屬於經典題還是面試官自己出的題目,反正我都沒見過,也沒準備,做起來是一樣的。不過如果有精力,建議多看看經典案例,真的能學到不少key point。

題目概括起來有四個要求:

  1. 所有查詢都是實時的
  2. 學生能檢視自己的排名
  3. 維護排名前100的排行榜
  4. 作業量每天清零,從新計算

第4點沒什麼意思,分庫分表,甚至每天清空一次資料庫都可以,可以不考慮。對於高併發的系統而言,可概括為兩個需求:

  1. 學生能實時查詢自己的排名
  2. 排行榜能實時更新

我設計的方案以一個桶為核心——之所以想到桶,多半是因為面試前還在複習ConcurrentHashMap的原始碼。ConcurrentHashMap是java.util.concurrent包中的一個神器,也算是面試常考點,你知道它的size()方法是怎麼實現的嗎?我忘記了,不過我在書裡的筆記討論了三種方案:

  1. 維護size變數;每次有段更新時,同步修改size變數。併發瓶頸在於size變數上的鎖,相當於退化回了序列更新。
  2. 不維護size變數,但維護每段的segSize;每次有段更新時,僅修改segSize;每次呼叫size()方法,把所有segSize加起來。可根據一致性要求選擇不同的segSize加和策略:要求完全一致性就在計算時所處所有seg,那麼呼叫size()方法時產生了併發瓶頸;要求弱一致性,可直接加和。
  3. 維護size變數;當segSize修改時,將size置為-1;呼叫size()方法時,如果size為-1,則重新計算size,同上;如果size不為-1,則表示期間未修改seg,直接返回size。方案3主要針對方案2,相當於快取了size值,這樣不需要在未修改segSize時重複計算size。

好好理解這三種方案,我的設計基於方案2.

需求1

對於需求1,相當於ConcurrentHashMap中呼叫size()方法的操作。不同的是,可能存在上千萬個學生,對每個學生都維護size顯然是不合適的,而不需要維護size的就只有方案2了。

具體來說,可以認為作業量有上限,一個學生不可能一天做無數作業。則可以維護一組有限的有序桶(桶內是否有序可根據讀寫比例調整),每個桶代表一段作業量的範圍(桶的範圍可根據併發規模設定,作業量頻率高的桶可繼續分成更小的桶,作業頻率低的桶可合併成更大的桶),桶之間不重合。則:

當前學生的排名 = 當前學生之前所有桶的size之和 + 當前學生在其桶內的排名

需求1的實時性要求一般沒有那麼高,因為使用者只看自己的排名,就算有一定延遲也不會影響使用者體驗,因此,每次使用者檢視排名都計算一次的消耗是可以接受的。當然,我們可以進一步優化,比如維護截止到每段開頭的總排名R,不過這需要增加一個服務實時去維護該段之後段的總排名R’,很可能是得不償失的。

需求2

稱作業量高的桶為大端桶,作業量低的桶和小端桶。對於需求2,相當於要維護大端桶中的前100個學生。由於假定作業量是有限的,則可以從最大作業量所在的桶開始遍歷,直到遍歷非空桶,且已按照作業量遞減的順序遍歷了100個學生為止,返回排行榜。

可以做一個優化——記錄當前最大作業量maxCnt,則最大桶的上界不超過maxCnt,且最大桶的size也不超過閾值sizeThreshold,那麼每次可直接從最大的空桶開始遍歷。

可以繼續優化——由於我們關心的是固定前100個學生,那麼直接維護一個最大堆maxHeap是更好的選擇。對於排行榜而言,顯然只有插入和查詢(取前100)操作,baseline模型相當於插入O(1)查詢O(nlogn);或者插入排序,則插入時O(n),查詢時O(1);而最大堆插入時O(logn),查詢時O(1)。

由於作業量高的學生總是極少數的,所以大端桶的併發量要遠小於其他桶,維護maxCnt和maxHeap的成本非常小,收益卻相當可觀。

最後,可以在maxHeap前加一層快取,非同步更新,以加速前端訪問。

follow up

在基本的架構介紹完後,面試官又給出了幾個follow up問題:

  1. 現在的服務都是單點的,如何解決單點故障?
  2. 你的桶要儲存在記憶體裡,發生了單點故障怎麼辦?

對於問題1,我把Hadoop的HA方案套上了。面試官似乎不瞭解Hadoop的內容,覺得我答的不錯。對於問題2,我清楚分析了不應該把桶與服務儲存在同一份記憶體中,而應該儘量讓服務無狀態,桶交給儲存層,不需要關心桶的結構。把問題2轉換成了問題3,“如何解決儲存的單點故障”。

首先,HA的方案仍然有效。但老一套沒意思,我就說可以換一種方案。服務端多例項,客戶端掛載服務端的例項列表,寫資料時讓客戶端維護服務端的資料一致性(全部寫才算寫成功),還順帶解決了讀資料時負載均衡的問題;用事務id解決客戶端寫失敗導致的一致性問題。

現在寫面經的時候才發覺這裡回答的並不好。這種方案的寫負載太高了,而併發寫時往往涉及同步問題,就更麻煩了。如果還是基於客戶端掛載的方案,那麼可以參照NRW策略,只需要保證R+W>N,就可以保證強一致性。不過總感覺還是不夠好,比如這種設計沒有考慮到可伸縮性,距離真正的分散式儲存系統差距還非常大。

PS:

  • N代表資料所具有的副本數。
  • R表示完成讀操作所需要讀取的最小副本數,即一次讀操作所需要參與的最小節點數目。
  • W表示完成寫操作所需要寫入的最小副本數,即一次寫操作所需要參與的最小節點數目。

總結

哎,面試完已經7點半了。從下午4點半開始,3個小時,還是挺熬人的,累累累,不過當場拿到offer十分滿足。

最後詢問面試官對我的評價:

  • 反應快——可能因為看出來我後面兩題都沒做過卻自己想出來了。
  • 程式碼寫的很好——受寵若驚啊,太不好意思了!!!
  • 系統設計也不錯,給出基本問題,能清楚設計出可行的方案,雖然可能沒有怎麼接觸業界的方案,但自己想的方案也比較完整。

二面的面試官是部門總監(創業公司的總監都相當年輕),這麼評價,程度上肯定有誇張了。不過方向上應該值得參考,幫助自己揚長避短。

我還詢問了今天面試跟正式校招的難度差距。面試官說跟校招難度類似,略微低一些,但差距不大。我挺驚訝的,都說猿題庫面試重演算法,比較難,面試之前我以為自己一道題就會被轟走,沒想到撐到了最後。。。第一場面試經歷如此甜美,幸福幸福~

給自己的建議:

  • 乖乖刷題,題量太小,碰到新題就龜速
  • 聽強爺的多練習表達,特別在系統設計上,如何做到條理清晰,吐字清晰,表現出自己的能力,這一點至關重要
  • 儘量不要裸面了,這次多虧面試前碰巧看了相關內容,否則可能就掛了
  • 菜雞,不要膨脹!不要膨脹!不要膨脹!

相關推薦

-2017825實習生

首先感謝熱心助人的崔同學,耐心給我講解猿題庫的面試風格,讓我能安心只准備了演算法和system design。不過演算法也沒準備,最近正常刷題而已;system design也只是複習了下搜狗的專案。相當於是裸面了。。。萬幸其他方面一點都沒有問,最後也拿到了實習

青鳥雲201882615:20:28

builder 完全 cto 就是 src sql 不同 查詢 class MyBatis用#引用參數 答:D mapper用來指定查詢sql Mappers元素是告訴MyBatis 去哪尋找映射SQL 的語句。 答:SQlsession不是線程安全的,所以不能

灰犀牛之2017821四川九寨溝發生7.0級地震

log 四川 image 技術分享 現在 cnblogs logs 記錄 2008年 北京時間8月8日21時19分46秒,四川阿壩藏族羌族自治州九寨溝縣(北緯33.20度,東經103.82度)發生7.0級地震,震源深度20千米,此後又發生多次余震。四川、甘肅、青海、寧夏、陜

傳智168期 day61 redis 筆記(201782519:16:30)

inux -s 入門 cnblogs com 操作 筆記 image 密碼 redis入門筆記,介紹了如何在Linux下搭建redis,開啟redis服務,還有常見的操作。 筆記下載: 鏈接:https://pan.baidu.com/s/1sl9GneP 密碼:

黑馬23期Linux+Nginx 筆記(201782519:12:50)

過程 style aid src linux環境 下載 http baidu span 主要是介紹在Linux環境下搭建nginx的過程。 筆記下載: 鏈接:https://pan.baidu.com/s/1o7KvxB8 密碼:7xki黑馬23期Linux+Ngin

2017814記錄 | 普及組

現在 增強 問題 相同 要求 使用 計劃 今天 size 寫在前面 今天登洛谷發現離Noip剩下88天了??(雖然看起有點久),然後覺得似乎水了一個暑假什麽也沒做(雖然學了點數據結構和一些奇奇Gaygay的東西),於是打開題庫發現去年Long Happy的集訓套題我似乎沒

基礎試題20-20171125

70、說一下Servlet的體系結構。所有的Servlet都必須要實現的核心的介面是javax.servlet.Servlet。每一個Servlet都必須要直接或者是間接實現這個介面,或者是繼承javax.servlet.GenericServlet或者javax.servl

2017725多校一Function

case pac lld esp break bool 題目 amp else Function這道題我當時一直很迷,到底怎麽來的啊,為什麽會這樣啊?? 然後看了題解才知道,原來是找循環啊。 已知f(i)=b[f(a(i)],則 f(0) = b

201789學習內容存放 #socket通信介紹

ssh port 綁定 服務端 logs 接收 數據鏈路 三次 hello 2017年8月9日學習內容存放 1 #socket通信介紹 2 3 ‘‘‘ 4 OSI七層 5 6 應用 7 表示 8 會話 9 傳輸 10 網絡 ip 1

2017817第二篇

服務 超全局數組 存在 積分 還在 目錄 生存 超全局 time PHP會話控制 跟蹤用戶的方式,在PHP裏面提供了三種跟蹤用戶的方式:一種通過URL一樣在後面附加參數來實現數據的傳說,第二種使用cookie將用戶的狀態信息存儲只客戶端的計算機裏面, 第三種是使用sessi

回顧(2017816 21:46:55)

最大 效率 alt tro 自己 utf-8 讀寫 編碼集 兼容 字符編碼: 2.0下默認是ascii碼,不能支持中文,所以中國有GB2312---GBK---UTF-8 utf-8是unicode(萬國編碼)的擴展集 GBK向下兼容GB2312 windows默認的編碼是

2017820第四篇

替換 正則表達式語法 pla 們的 模式 six 而是 als 可能 PHP正則表達式 一什麽是正則表達式:正則表達式就是一種描述字符串結構的語法規則。 二為什麽需要正則表達式? 因為需要對用戶提交的信息進行驗證,如果不驗證的話,有可能用戶提交的就是垃圾信息。 三PHP裏面

Linux 系統磁盤分區知識(2017830 11:47:02)

創建文件 裝系統 主分區 prim mar 數據 中一 ges com Linux分區知識 主分區,擴展分區,邏輯分區 1、一塊硬盤最多可以有4個主分區(primary),其中一個主分區的位置可以用一個擴展分區(extend)替換,只能有一個擴展分區,擴展分區內可以劃分多個

Linux分區重要知識介紹等(2017830 16:50:49)

格式化 bsp 2017年 str log tro http nbsp es2017 Linux分區重要知識介紹 格式化-創建文件系統 Linux分區重要知識介紹等(2017年8月30日 16:50:49)

2017510澳大利亞墨爾本港資料中心發生火災

澳大利亞退休金提供商UniSuper公司的客戶在墨爾本港資料中心遭到火災後無法訪問他們的帳戶。幸運的是在這次火災中沒有資料丟失或洩露。 “5月10日早上,我們的一個數據中心的事件觸發了部分系統關機。我們的一些系統和服務為此受到了影響。”UniSuper公司發言人凱瑟琳·多爾曼在接受媒體iTnews採

2017413網際網路之父羅伯特-泰勒(Robert W. Taylor)去世

據《紐約時報》報道,網際網路發明者之一羅伯特-泰勒(Robert W. Taylor)週四在加州伍德賽的家中離世,享年85歲。 跟其他許多創造發明一樣,網際網路的誕生是許多人共同努力的結果。不過,發明這項改變世界的技術的最大功勞也許應當歸屬羅伯特·W·泰勒(Robert W. Taylor)。 他

把chrome請出系統盤還讓它自動更新(2017811有更新)

2017年8月11日更新     這兩天發現,要想把chrome請出系統盤並讓它自動更新,有比最初回答(在下面)更簡單的辦法。     辦法就是一句話:在安裝前,用mklink把兩個連線做好。連結做好

學習筆記2017718MySQL測試:模擬QQ數據

關系 ref sts one database 等級 weight insert phone 模擬測試: QQ數據庫管理 一、創建數據庫並添加關系和測試數據 1 ##創建QQ數據庫,完成簡單的測試 2 3 #創建數據庫 4 DROP DATABASE IF EX

OCP|052OCP更新052最新考題及答案整理-第10

action media recovery about arc cover pen shutdown data 10、Which two are true about consistent database backups? A) They can only be take

NLP演算法實習生--9.11

感覺剛開學這段時間真的是面試最水的時期了,想當初面暑期的時候真的被狂虐~ 9.11上午新浪的演算法實習生(給了口頭offer) 實習僧上投的簡歷,本來上面寫的要求是碩士以上,試著投了投就約了面試 (一面) 坐下來他才開始看我的簡歷,想著主動點,就主動做