面試題-酷家樂面試准備


已經准備的面試題

12月5日 刷完Java基礎和MysqlRedis

剩余Spring Mybatis Netty RabbitMQ Storm面試題


智力題

資料

常見智力題

1000瓶酒找毒酒問題

  • 題目:其中一瓶有毒,一旦吃了,都會在一周后發作,現在我們用小老鼠做實驗,要在1周后找出那桶毒酒,問最少需要多少只老鼠?
  • 題解:用10只老鼠即可。把這個問題轉換成二進制數就好理解了,第一桶到第1000桶都對應着一個二進制的數字,每個老鼠對應二進制的一位。對每一桶酒來說,把二進制位上為1的給對應位置的老鼠喝。等到毒發時,統計哪幾位上的老鼠死了,然后轉換成十進制就可以了。
  • 資料:資料

等概率選擇商店問題

  • 題目:有3家商店,手上有一個硬幣,怎么等概率地選擇一家商店;注意:正反出現的概率相同,都為0.5
  • 題解:用二進制來理解三家商店,01 10 11,分別代表三家商店,拋兩次硬幣就可以了

量水問題

  • 題目: 用3升,5升杯子怎么量出4升水?
  • 題解:
    • 用5升杯子裝滿水,倒入3升杯子,還剩2升
    • 把2升倒入3升杯子,然后把5升裝滿
    • 倒入3升杯子,5升杯子就剩了4升

找出較重的球

  • 題目:假設你有8個球,其中一個略微重一些,但是找出這個球的惟一方法是將兩個球放在天平上對比。最少要稱多少次才能找出這個較重的球?
  • 題解:分成3+3+2
    • 第一次稱3和3
      • 如果相等,證明重的球在2個球的堆里,再稱一次就能得出結果
      • 如果不相等,從較重的那堆3個中,隨機那兩個稱重就能得到最重的球

賽馬問題(參考資料:Google-25匹馬的角逐

  • 題目:一共有25匹馬,有一個賽場,賽場有5個賽道,就是說最多同時可以有5匹馬一起比賽。假設每匹馬都跑的很穩定,不用任何其他工具,只通過馬與馬之間的比賽,試問最少 得比多少場才能知道跑得最快的5匹馬。

  • 第一種解法

    1. 將25匹馬分成5個組進行比賽,A組到E組,得到每組的第一名A1~E1
    2. A1~E1進行比賽,得到第一名,假設是A1
    3. A2,B1,C1,D1和E1進行比賽,可以得到第二名
    4. 依次類推,一共需要比賽10場就能得到前五名的馬
  • 第二種優化解法

    優化方式:

    • 讓一些馬提前淘汰
    • 能夠通過一場比賽決定出更多的名次

    第一步和第二步必不可少,前6場比賽后,B組的最后一名,C組的最后兩名,D組的最后三名,E組的最后四名這些馬已經提前淘汰了。

    第七場比賽,通過A2和B1就可以決出第二名,剩下三個名額是否可以同時決出第二名和第三名?

    • 如果A2>B1,那么第三名一定在A3和B1中產生
    • 如果B1>A2,那么第三名在A2,B2,C1中產生

    所以第七場比賽,讓A2,B1,B2,C1,A3比賽即可決定第二名和第三名

    根據第七場比賽的所有可能結果,可以繼續按照這個方法推測每場比賽需要的比賽馬

稱鹽問題

  • 題目:140g鹽,只有一個2g砝碼和7g砝碼,還有一個天平,稱三次,分成50g和90g兩堆。
  • 題解
    1. 先用天平分成70g的兩堆
    2. 用2+7g的砝碼稱出9g的鹽
    3. 2g的砝碼和9g的鹽稱出50g的鹽

燒香問題-確定15分鍾

  • 題目:兩個棍子,長度不一,粗細不一,質量密度都不一。但一小時能燃燒完。我如何確定15分鍾。
  • 題解:A棍子點兩頭,B棍子點一頭,等到A棍子燒完之后,再點B棍子的另一頭,從開始點到燒完既是15分鍾。

燒香問題-確定45分鍾

  • 題目:兩個棍子,長度不一,粗細不一,質量密度都不一。但一小時能燃燒完。我如何確定45分鍾。
  • 題解:A棍子點兩頭,B棍子點一頭,開始計時,等到A棍子燒完之后,再點B棍子的另一頭,燒完整個時間是45分鍾。

N個人找明星問題

  • 題目:有N個人,其中一個明星和n-1個群眾,群眾都認識明星,明星不認識任何群眾,群眾和群眾之間的認
    識關系不知道,現在如果你是機器人R2T2,你每次問一個人是否認識另外一個人的代價為O(1),試設計
    一種算法找出明星,並給出時間復雜度(沒有復雜度不得分)。
  • 題解:從頭開始遍歷,1號如果不認識2號,那么2號不是明星;1號如果認識2號,那么1號不是明星。從頭遍歷到尾,剩下的那個人就是明星,時間復雜度O(N)。

判斷一個點在三角形中問題

  • 題目:如何判斷一個點在三角形中
  • 題解:
    • 第一種:角度和,把p連接三角形的頂點,分別求角度和,如果為180°,則P點在三角形內
    • 第二種:向量的叉乘。
      • P和C在AB的同側
      • P和B在AC的同側
      • P和A在BC的同側。

放棋子必勝問題

  • 題目:一個圓,有黑白兩種棋子,兩個人放棋,一個一次只能方一枚棋子,最后放不了那個人算輸,請給出一個必勝的方案

  • 題解:我方先放,放在圓心即可。

    因為其他棋子都圍繞圓心中心對稱,所以我方會多一個棋子。

沙漠加油問題

  • 題目:你一次只能最多帶走60km的油,只能在起點加油,如何穿越80km的沙漠?
  • 題解:
    • 第一次裝滿60km的油,然后走到20km處,卸下20km的油,再返回起點
    • 在起點再加油60km,到達剛才的20km處時,你還剩40km的油,加上剛才放的20km的油,即可走完全程。

算法題

兩個單鏈表相交,怎么求交點。所謂相交,就是兩個節點的next指針相同

簡單解法:分別遍歷兩個單鏈表,把元素分別存儲到一個List中。然后倒敘遍歷List,得到第一個不同的節點的下一個節點就是交點。時間復雜度O(N),空間復雜度O(N)
優雅解法:分別遍歷兩個鏈表獲取長度,讓長度長的那個鏈表,先走長度差步,然后一起走,兩個指針相遇的節點就是交點。時間復雜度O(N),空間復雜度O(1)

如何判斷兩個單鏈表是否相交?

從頭遍歷到尾,然后判斷最后一個節點是否相同,如果相同則相交。

給你一個數組,代表每天股票的價格,比如【3,5,4,6,2,5】表示6天的價格,問如果只能買一次,並且提前知道每天的價格,最大收益是多少

O(N)的時間復雜度就可以算出來。

雙指針,一個head,一個tail,tail從頭開始遍歷,如果tail的值大於等於head,計算一次max;如果tail<=head,不計算,讓head=tail,然后繼續往后遍歷。遍歷結束就可以得到最大的收益。

給n個有序數組,求一個區間[a, b],確保每一個數組至少有一個值在區間內,並使區間最小。

還是用最小堆來解決問題。

先把每個數組的第一個值拿出來生成第一個最小堆。

然后把堆頂元素剔除,從被剔除的堆頂元素數組中拿下一個元素放入堆中,重新生成最小堆。

直到某一個數組元素遍歷到末尾。

設計一個循環隊列,區別於arraylist的擴容機制,空間可重用。

隊列滿的條件:(write + 1) % arr.length == tail

隊列空的條件:tail == write

找一個無序數組的中位數

解法一

利用排序,寫完排序后,根據數組的容量是奇數還是偶數,直接取中位數即可。

解法二

可以利用小頂堆實現。

  1. 取數組的前半數據建立小頂堆
  2. 數組的后半部分每個數據與小頂堆的頂比較
    1. 如果小於等於拋棄
    2. 如果大於等於,入堆,最后得到的堆頂就是中位數

堆的介紹

堆是完全二叉樹。(滿二叉樹,除了最后一層,每一層都有兩個孩子節點;完全二叉樹不要求滿,但要求節點從左到右,不能有空襲位置沒有子節點)

完全二叉樹,可以給每一個節點編號,從上到下,從左到右,對任意一個節點i,它的父節點的編號都為i/2,它的左孩子節點都為2*i,右孩子節點2 * i +1

所以我們可以使用數組來存儲,編號對應的就是數組的位置。

|a-b|+|b-c|+|c-a|的最大值或者最小值

題目

給三個數組,從三個數組中選擇三個數,求|a-b|+|b-c|+|c-a|的最大值或者最小值

題解

假設a>=b>=c,那么化簡之后的結果為

(a-b) + (b-c) + (a-c) = 2(a-c) 也就是說,結果只與這三個數的最大值和最小值相關。

數據流中如何取前十個小的數

用大頂堆來解就可以了。

先用數據流構建一個10長度的大頂堆,然后數據流后面的數據每一個和大頂堆的堆頂對比,如果比堆頂大,拋棄,如果比堆頂小,替換堆頭,然后重新排序保證堆為最大堆。

MySQL的排序如果有limit N,內部會使用這種算法來進行排序。

如何判斷單鏈表里有無環 並且找到環的頭節點

資料:鏈表中是否有環以及找環的入口問題總結

判斷有無環的方法:

用快慢指針,快指針一次走兩步,慢指針一次走一步,如果兩個指針最終相遇,說明有環。

證明:用歸納法證明。假設慢指針剛進入環時

  • 快指針在慢指針后面的一個節點,那么再走一步,二者相遇
  • 快指針在慢指針后面的兩個節點,那么再走兩步,二者相遇

找到環的頭節點方法:

算法:相遇后,在head中再加入一個指針,從頭開始走,慢指針也繼續往前走,速度一樣,相遇點即為環的頭節點

證明:

img

先證明,快慢指針相遇時,慢指針還沒有走完一圈

假設慢指針入環時,快指針距離慢指針還有M步,由上面證明可知再走M步快慢指針必相遇,因為M小於環的周長R(因為如果等於的話,就直接相遇了,不需要追擊,慢指針進入環時,快指針一定在環的某個位置),所以慢指針還沒有繞環一圈。

慢指針走過路程:S = lenA+x;

快指針走過路程:2S = lenA + x + n*R(可能已經走過N圈了)

化簡得到 lenA = n*R-x

我們想要證明 慢指針在相遇后走了S+ 然后再走lenA就能到達join點,所以把上述式子左右兩邊+S

S+lenA = S + nR - x = S+ (n-1) * R + R-x = S+(n-1)R + y

這里的 S + (n-1)*R 就是相遇點,所以y = lenA

兩線程交替打印數字

核心設計:

  • 兩個線程爭搶一個鎖

  • 拿到鎖后打印數字

  • 加數字

  • notifyAll

  • wait

 while (i <= 100) {
            System.out.println(Thread.currentThread().getName() + ":" + (i++));
            notifyAll();
            wait();
        }
 notifyAll();//這行需要加,否則永遠不會停止

數據結構題

樹相關

二叉樹鏡像:前序遍歷,如果存在左右節點,就交換,然后遞歸左節點,最后遞歸遍歷右節點。

前序遍歷:停止條件是,當前節點為null,直接返回;先print,然后遞歸遍歷左子樹,最后遞歸遍歷右子樹

中序遍歷:停止條件是,當前節點為null,直接返回;先遞歸遍歷左子樹,然后print,最后遞歸遍歷右子樹

后序遍歷:停止條件是,當前節點為null,直接返回;先遞歸遍歷左子樹,然后遞歸遍歷右子樹,最后print

二叉排序樹(因為有序:查找效率不錯;因為非線性結構:插入和刪除效率也不錯)

要么是一棵空樹,要么是 一棵符合下面性質的樹

  • 若左子樹不為空,左子樹所有節點值都小於根節點的值
  • 若右子樹不為空,右子樹所有節點值都大於根節點的值
  • 左子樹和右子樹也分別是一棵二叉排序樹

查找:停止條件,一是當前節點為null,返回false;一是當前節點等於待查數據,返回true;其他情況需要進行遞歸左子節點或者遞歸右子節點來實現。

插入:先查找,如果失敗,和當前節點比較,如果節點為null,待插入節點為根節點;如果小於節點值或者大於節點值,設置為節點的左孩子或者右孩子。

刪除:

  • 刪除節點是葉子節點,直接刪除即可
  • 刪除節點只有左子樹或者右子樹,直接用子樹子承父業即可
  • 刪除節點既有左子樹又有右子樹,找到待刪除節點的前驅節點,交換,然后刪除前驅節點即可

B樹

為什么需要多路查找樹?

當引入硬盤的概念時,我們就更需要考慮IO操作的次數,因為IO操作是整個流程的短板操作。之前學習過的樹,每個節點只能存儲一個數據,在元素非常多的時候,要么度很大(子節點很多),要么高度很大,這兩種方式都會導致更多的IO操作次數。

多路查找樹:每個節點可以存儲多個數據並且節點的孩子數可以多於兩個。

B樹是一種平衡的多路查找樹,非葉節點至少有兩個孩子,每個節點可以包括k-1個元素和k個孩子,所有葉子節點都在同一層。在具體設計的時候,可以把b樹的節點大小和硬盤的頁大小匹配,讓一次IO操作盡可能讀取更多的有序元素進行比較。

B+樹

B+樹的出現是為了優化B樹的遍歷需求(范圍查找),和B樹不同,

  • B+樹的每個葉子節點中還保存了父節點中的索引值
  • B+樹的每個葉子節點還有下一個葉子節點的索引

這讓范圍查詢和遍歷都變得更加方便。

排序相關

冒泡排序

原理:從后面往前,兩兩比較,如果較小就往上冒。

代碼簡述:兩個循環,第一層是i=0到最后一個值,第二層是j=最后一個值,j--,直到j<i

簡單選擇排序

原理:每一趟在 n-i+1(i=1到n-1)個記錄中,找到最小的記錄,作為有序序列的第i個記錄。

代碼簡述:兩層循環,第一層i=0到最后一個值,第二層j=i+1到最后一個值,找到最小值,如果需要交換就交換

直接插入排序

原理:將一個記錄插入到已經排好序的列表中

代碼簡述:兩層循環,第一層是i=1到最后一個,第二層循環是往前找待插入位置

系統設計題

海量用戶積分排名算法探討
某海量用戶網站,用戶擁有積分,積分可能會在使用過程中隨時更新。現在要為該網站設計一種算法,在每次用戶登錄時顯示其當前積分排名。用戶最大規模為2億;積分為非負整數,且小於100萬。

第一種方式:用一條簡單的SQL語句查詢出積分大於該用戶積分的用戶數量
優點:實現簡單
缺點:全表查詢,性能差

其他方式需要再研究

相關資料

簡單描述一下怎么實現一個rpc框架
RPC框架:客戶端調用服務端方法,就像調用本地方法一樣。
對客戶端來說,使用某種方式獲取依賴的服務接口的一個對象,然后調用需要的方法。
對服務端來說,使用某種方式,把實現了接口的對象注冊到RPC框架中。

整個調用流程:
客戶端獲取的是服務端真實類的一個樁,這個樁是一個代理類
樁內部的代碼就是把請求的服務名和參數發送到服務端,服務端解析對應的服務名,傳入參數來調用真正的方法,把方法返回值返回給樁,樁再把返回值返回給客戶端。

關鍵設計:

  1. 注冊中心(服務端需要把服務名和自己的地址注冊到注冊中心;客戶端需要根據服務名從注冊中心獲取服務端的地址)
  2. 客戶端需要的接口-獲取一個遠程服務實例
  3. 服務端需要的接口-注冊自己的服務到框架中

**圖書管理系統,每人一張卡,最多能借十本書,一本書最多能借30天,系統包括借書和還書,借書超過30天會計算罰金,罰金沒還完無法繼續借書。此外后面還加了一個預約功能。(基本就是從數據庫表設計去回答了) **

火車票購票,分別有購票、退票、檢票三個功能,一共五十張票,有文件A和文件B,文件A記錄買票人信息,文件B記錄檢票人信息,模擬真實場景進行設計,需要考慮各種異常情況。(面試官最后說這個題有一個比較重要的考察點就是緩存和文件的讀寫順序)
需求分析
功能性需求
需要討論的點?
有文件A和文件B,文件A記錄買票人信息,文件B記錄檢票人信息,這個是什么意思?是需要把相關信息存儲到文件中?假設是確定用文件系統來保存數據,並且確定了表存儲的內容,並非固定的結構
是否已經默認登陸了系統?假設為是;
是否需要做支付?假設不需要,默認用戶已經支付,我們只考慮如何保存相關業務信息即可;
退票業務是否支持發車后退票?是否支持發車前N分鍾內無法退票的情況?假設不支持發車后退票,並且發車前15分鍾內不支持退票

  1. 購票業務:用戶點擊購票按鈕,生成訂單,訂單中包含始發站,終到站,發車時間,車次信息
  2. 退票業務:用戶點擊退票按鈕,檢驗是否可以正常退票,如果正常,提示用戶退票成功,否則提示用戶退票失敗
  3. 檢票業務:用戶到檢票地點進行檢票,系統內部需要檢查該票的狀態是否可以檢票,如果狀態正常,更新相應信息,返回檢票成功或者失敗

非功能性需求

  1. 系統需要保證支持高並發(使用我們系統的人數可能較高)
  2. 系統要保持高可用(如果系統掛了,那么用戶就無法購票乘車;或者人工窗口會產生大量業務)
  3. 盡量保證低延遲的處理每個用戶的請求

相關預估
流量預估
需要詢問面試官,系統的購票流量大約有多少?

分析可得知,系統的購票流量一定遠大於退票流量,幾乎等於檢票流量
假設系統的購票流量為100張/s,退票流量按照5%的退票率,一共有50張票,那么每秒退票流量2張/s,檢票流量為50張/s

帶寬預估
因為流量較低,所以帶寬幾乎不需要進行預估

存儲預估
系統中設定需要使用文件保存相關信息,因為票量較少,所以這里也不進行預估

SystemAPI設計

  1. orderId buyTicket(userId,TicketInfo)
  2. refundTicket(orderId)
  3. checkTicket(orderId)

數據庫表設計
車站表:車站id,車站名
車輛表:車輛id,總座位數
車次表:存儲車輛id,始發站車站id,發車時間
車次詳情表:車次id,途徑站車站id,是否為本次車次的終點站,到站時間,發車時間(如果為終點站,發車時間為null)

購票表:購票表id,用戶id,車次表id,上車站id,終到站id,票數,訂單狀態(所有票都是 已檢票 或者 退票 才為已結束,其他狀態進行中)
購票詳情表:購票id,座位號,狀態(未檢票,已檢票,退票)

注意:任意分段余票的算法,可以通過
T(i,j) = 車輛總座位數 - (Si ∪ S(i+1) ∪ … ∪ S(j -1)) (Si為i站到i+1站的占用座位數)
詳細資料:火車票余票問題的算法解析

關鍵業務流程設計
購票是所有業務的起點業務,購票時最核心的操作是要校驗所選分段是否有余票,如果有,創建座位占用表相關數據,余票算法可以通過上面的算法公式計算得到,最后創建購票表
校驗是否有余票的業務需要加同步鎖,具體設計可以參考這里:淺談庫存扣減和鎖

退票業務
待完成

Redis MQ MySQL 在系統中的使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM