面試題-酷家樂面試準備
已經準備的面試題
12月5日 刷完Java基礎和MysqlRedis
剩餘Spring Mybatis Netty RabbitMQ Storm面試題
智力題
資料
量水問題
- 題目: 用3升,5升杯子怎麼量出4升水?
- 題解:
- 用5升杯子裝滿水,倒入3升杯子,還剩2升
- 把2升倒入3升杯子,然後把5升裝滿
- 倒入3升杯子,5升杯子就剩了4升
找出較重的球
- 題目:假設你有8個球,其中一個略微重一些,但是找出這個球的惟一方法是將兩個球放在天平上對比。最少要稱多少次才能找出這個較重的球?
- 題解:分成3+3+2
- 第一次稱3和3
- 如果相等,證明重的球在2個球的堆裡,再稱一次就能得出結果
- 如果不相等,從較重的那堆3箇中,隨機那兩個稱重就能得到最重的球
- 第一次稱3和3
賽馬問題(參考資料:Google-25匹馬的角逐)
-
題目:一共有25匹馬,有一個賽場,賽場有5個賽道,就是說最多同時可以有5匹馬一起比賽。假設每匹馬都跑的很穩定,不用任何其他工具,只通過馬與馬之間的比賽,試問最少 得比多少場才能知道跑得最快的5匹馬。
-
第一種解法
- 將25匹馬分成5個組進行比賽,A組到E組,得到每組的第一名A1~E1
- A1~E1進行比賽,得到第一名,假設是A1
- A2,B1,C1,D1和E1進行比賽,可以得到第二名
- 依次類推,一共需要比賽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兩堆。
- 題解
- 先用天平分成70g的兩堆
- 用2+7g的砝碼稱出9g的鹽
- 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的油,即可走完全程。
演算法題
給你一個數組,代表每天股票的價格,比如【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
找一個無序陣列的中位數
解法一
利用排序,寫完排序後,根據陣列的容量是奇數還是偶數,直接取中位數即可。
解法二
可以利用小頂堆實現。
- 取陣列的前半資料建立小頂堆
- 陣列的後半部分每個資料與小頂堆的頂比較
- 如果小於等於拋棄
- 如果大於等於,入堆,最後得到的堆頂就是中位數
堆的介紹
堆是完全二叉樹。(滿二叉樹,除了最後一層,每一層都有兩個孩子節點;完全二叉樹不要求滿,但要求節點從左到右,不能有空襲位置沒有子節點)
完全二叉樹,可以給每一個節點編號,從上到下,從左到右,對任意一個節點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中再加入一個指標,從頭開始走,慢指標也繼續往前走,速度一樣,相遇點即為環的頭節點
證明:
先證明,快慢指標相遇時,慢指標還沒有走完一圈。
假設慢指標入環時,快指標距離慢指標還有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到最後一個,第二層迴圈是往前找待插入位置
系統設計題
如果你去超市購物。描述你在超市購物這個過程。有哪些實體集及其屬性。
描述下12306購票系統(查票、購票、退票)