從一道面試題談?wù)勔痪€大廠碼農(nóng)應(yīng)該具備的基本能力
作者:Yura Shevchenko 來源:skypixel.com
關(guān)于一線碼農(nóng)的面試,我想說
求職面試在絕大部分人來說都是必不可少的,自己作為求職者也參與了不少面試(無論成功或者失?。?,作為技術(shù)面試官參與面試也有四五年的經(jīng)驗,在面試過程中也見識到了各種各樣的人(有厲害的,也有奇葩的)。在這里也只想談?wù)勛约旱囊恍┛捶ǎ?strong>我說的不一定對,有不同的意見可以留言參與討論。
面試本來就是一個雙向選擇的過程,面試官和候選人的地位本應(yīng)該是一個平等的位置,面試官希望通過簡單的交流溝通可以對候選人的技術(shù),溝通等有一定了解進而確定候選人是否匹配相應(yīng)的職位。個人認(rèn)為一場成功的面試最好是能夠讓求職者和面試官都有一定的收獲(曾經(jīng)也遇到過在某次面試后,HR 告訴我有候選人特意跟她反饋要表達對面試官的感謝,因為讓他很有收獲,這當(dāng)然還是讓我感到非常高興的),每次參與面試,也希望自己能達到這個目標(biāo)。對于候選人來說能從面試過程了解自己的不足或者交流探討面試問題;對于面試官來說能了解候選人的技術(shù)和項目,在交流探討中也是一次學(xué)習(xí)和鞏固。 另外面試能否通過最終強調(diào)的是職位匹配,一個蘿卜一個坑,蘿卜太大或太小都不一定合適。所以有時候面試沒通過并不是候選人不夠優(yōu)秀,也有可能是候選人過于優(yōu)秀(例如本來只想招聘 P6,結(jié)果來了一個 P8的候選人肯定不合適)。
因為面試時間有限,1個小時(一般情況)的時間很難去全面了解候選人的技術(shù)實力,因此在面試過程中很難做到絕對的公平。舉個簡單的例子,面試官出一道題目,候選人 A 可能曾經(jīng)做過或見過,所以能夠比較輕松地回答出這個問題,而候選人 B 沒有做過,雖然不能答出讓面試官滿意的答案,但 B 提供了一些解題的思路,雖然最終并沒有答出這道題目,這就一定說明候選人 B 比 A 差么? 并不是吧。
下面就從這道題目說起,這道題目是我在過往的面試中經(jīng)常考察的一道題目。
動筆試試吧
實現(xiàn)一個函數(shù),完成 開根號 的操作,方法簽名如下:
double sqrt(int v, double t)
要求:
不能調(diào)用系統(tǒng)庫函數(shù),諸如
Math.sqrt(v)
之類的;假設(shè)計算出的結(jié)果為
r
,要求滿足這個條件:,其中
是真實的值, t 為給定的一個誤差范圍,例如0.1等,即你計算出的值要在給定的誤差范圍內(nèi)。
實現(xiàn)語言不限,你條件可以比上述更加苛刻,但不能寬松。例如調(diào)用你的接口
sqrt(9, 0.21)
返回值屬于[2.79, 3.21]
這個區(qū)間的任意一個都滿足條件。
看到這里,其實你可以 拿出筆和紙,嘗試解答一下,需要注意的是答案要滿足給定的誤差條件,歡迎溝通交流。其實這個題目是就是 leetcode 上原題稍加變化得到,做過的肯定覺得 leetcode 其他題目來說相對比較簡單。但沒做過也沒關(guān)系,如果在面試官的提示下能夠最終把這道題目解出來,在我看來也 OK 的,甚至有可能比刷過題記住解題答案的更好(當(dāng)然刷過題目本身的肯定會圍繞這個題目穿插其他小問題的)。
開始解答
其實剛開始,我認(rèn)為這道題目比較簡單,至少在給予提示后,理想情況下大部分一線coding的程序員都可以給出實實在在 code 的。然而“理想很豐滿,現(xiàn)實很骨感”,事實并非如此,然而在面試很很多人之后, 發(fā)現(xiàn)此道題目并不簡單,如果你能寫出來,說明你已經(jīng)比很多人優(yōu)秀了(至少在我過往的社招面試經(jīng)歷中)。
當(dāng)被問起這道題目之后,可能遇到的答案如下。
直接放棄
題目給出后,我一般首先明確候選人弄清楚了題目的含義然后會給一兩分鐘讓候選人先思考一下。
面試官:你有什么思路嗎?
求職者: 沒有啊。
可能候選人內(nèi)心OS是: “你出這樣的題目是不是有病啊,明明有 lib 函數(shù)可以直接用的”。(之前同組有其他小伙伴確實有遇到這樣的候選人,語言雖沒這樣夸張,大意是:實際工作中會出現(xiàn)這樣的問題嗎? 我直接給你百度一個就行了)
在此強調(diào),面試這道題目并不是想強調(diào)這個題目本身,期望以這道題目為契機,考察候選人在分析問題和解決問題的能力,在交流過程中所體現(xiàn)的邏輯推理和思維方式等,當(dāng)然最后也會看看實實在在的 Code,從編碼過程中看候選人的編程習(xí)慣,風(fēng)格等等。
也有候選人剛開始抱著那個約束誤差范圍的不等式研究 N 久,然后沒有然后了的。剛開始看這個條件當(dāng)然好,但如果這個不等式?jīng)]有思路可以先放一放,沒必要在那苦熬。
面試官:這樣吧,如果我問題 根號10 等于多少?你怎么回答?
求職者:3點幾吧。
面試官:你怎么知道是3 點幾,因為你知道9開根號是3,想象一下,你也可以完全用程序幫忙模擬你大腦思考的過程。
求職者: 我再想想……
其實這里是希望提醒候選人,我們首先是要解題,然后才考慮效率。即不管用什么方法能夠給出一個答案的,這個時候候選人可能進入下一個階段了。
在實際工作場景中其實也是一樣,遇到一個問題,首先我們要想到的是如何解決這個實際問題,有了最基礎(chǔ)的解決方案之后再談優(yōu)化。
暴力搜索
實際面試過程中也有人是直接到這個階段的。
先用一個循環(huán)找到 r,使得 r^2 是離給定 v 最近的平方數(shù),即你希望算根號10 ,先找到3,因為3^2=9 。
然后再用一個循環(huán), 每次 r+=t ,直到 r^2 > v 為止。
面試官:這個方法從理論上講, 是一個可行的方案,設(shè)想一下,如果我的精度要求很高,希望計算的 v 也很大,如
sqrt(v = 10000000, t = 0.000001)
之類的,調(diào)用你這個方法效率是不是很低,這個時候應(yīng)該怎么優(yōu)化?
求職者:這樣的話,我這個方法效率確實比較低,不過可以這樣優(yōu)化,比如設(shè)置一個步長,一次迭代后,如果沒有達到預(yù)期,可以不斷修改這個步長來增大逼近真實值的速度,比如10倍誤差,100倍誤差等。
其實,在與候選人的不斷交流中可以看出候選人的 Problem Solving
的能力,這也是面試考察中的一點。例如關(guān)于上面問題的優(yōu)化,也可能用于在實際工作中遇到的問題。
例如,我們在實際工作中可能經(jīng)常會寫一些異步的回調(diào)通知接口等,這個接口可能是其他團隊維護的,有可能由于網(wǎng)絡(luò)問題等回調(diào)接口可能會失敗進而需要重試,對于重試的機制其實就可以借鑒上面的“步長”機制,第一次回調(diào)失敗, 我等待 1s 后重試,失敗再重試,也許間隔 1s 不太恰當(dāng),是否可以修改等待的步長,等待比如 5s,10s?等等再重試直到成功。為什么要這樣做? 也許對方 server 本來現(xiàn)在就處于峰值,你不停的重試不但沒有增加你接口調(diào)用成功的機會,反而增加對方 server 的負(fù)擔(dān)。
額,跑題了,回到這個問題本身,繼續(xù)
面試官:恩,這樣做確實可以優(yōu)化。但從本質(zhì)上講,假設(shè)我們不考慮誤差的話,這個題目其實就是在一個有序的列表里面去搜索滿足條件的特定的值。剛剛你的方法是一個線性的搜索方案。常見的搜索還有其他什么嗎?
求職者:二分搜索?
面試官:對呀,你可以嘗試想想能否借用一下這個思路來解決這個問題。
二分搜索
當(dāng)然,部分候選人提示二分后,就直接能夠 get 到點,并且能夠?qū)懗龆执篌w框架,但可能結(jié)束條件寫的有點問題。
如果候選人還沒有思路,就會繼續(xù)
面試官:這樣理解吧,你剛剛的搜索整數(shù)部分的過程其實是線性的,一個一個數(shù)去暴力窮舉。借助二分的意思就是,比如算 根號10,你搜索范圍是 [0, 10] (其實除了幾個數(shù)之外范圍可以更小[0, v/2],你能證明么?)。
因為5^2 = 25 > 10 , 所以 r 屬于[0, 5)
因為(5/2) ^2 = 6.25 < 10 , 所以 r 屬于 (2.5, 5)
因為(3.75^2 = 14.0625 > 10) ,所以 r 屬于 (2.5, 3.75)
繼續(xù),如果你結(jié)束條件不太確定,可以暫時不管…
提示到這里,感覺已經(jīng)相對比較明顯了。
面試官: 你現(xiàn)在明白了嗎?
求職者:明白了。
面試官:那你寫一下代碼吧。
一個二分查找,算法思路都結(jié)合例子講一遍了,在候選人回答明白的情況下,理想當(dāng)中,作為一線開發(fā)者寫出來應(yīng)該不成問題吧。然而…理想和現(xiàn)實還是有差距的。
很多人都喜歡用遞歸寫,可是很多人遞歸里面的最重要的結(jié)束條件都木有, 一些邊界條件等等都木有。所以一般情況下,代碼寫完后,我會讓候選人自己寫測試用例。
面試官:寫好了是吧,你寫幾個測試用例吧,假設(shè)這個接口是別人寫的,你應(yīng)該從哪幾個角度去測試。
求職者:sqrt(-4, 0.21),哎呀,我這里忘了判斷了。改一下代碼。
求職者:sqrt(0, 0.21),sqrt(4, 0.21)… 還有問題,再改改。
面試官:……
為什么要別人提示要測試用例,才去 check 自己寫的代碼的正確性呢。有的候選人寫的代碼,就不拿一些異常情況去 check,就用上面講的 sqrt(10, 0.21)
的例子都得不到預(yù)期結(jié)果。
能夠到達這一個步驟的人已經(jīng)較少了,如果你有較全測試用例和邊界條件的判斷,再加上后面的結(jié)束條件能夠正確,基本上這道題目就算滿意了。
關(guān)于結(jié)束條件
本質(zhì)上講,這個算法就是一個迭代逼近的過程,用二分的思路后,關(guān)鍵就在于什么時候結(jié)束。 題目中已經(jīng)給了誤差條件 ,難點在于其中的
不知道,不太方便直接進行計算判斷。不少人用一個另外的結(jié)束條件來進行了判斷即:
,其實這兩個條件是不一樣的。
對于這個結(jié)束條件,你有什么看法嗎? 能證明你的想法嗎?
其他解法
當(dāng)然本題還有一些其他的數(shù)學(xué)解法,例如用牛頓迭代法,梯度下降法(最速下降法),泰勒公式展開等等。如果候選人能想到這些,說明他還是有一定數(shù)學(xué)基礎(chǔ)的,如果愿意可以讓他講講。(考察這道題目本意并不是期望候選人用這些數(shù)學(xué)方法解的。)
對于這道題目,你有什么比較好的思路嗎? 歡迎留言參與討論。
常見問題
問:為什么題目中的
v
的類型是int?
答:還真沒有理由,double
也無所謂,可能僅僅是因為 leetcode 上原題計算的數(shù)是int
吧。問:我能正確答對這道題目就一定能通過這次面試嗎?
答:強調(diào)一下,面試中考察這樣一個題目,并不是僅僅考察這道題目本身,不是說你將這道題做對了,就能通過面試,反之也不是說你沒做對這道題目就一定不能通過我們的面試。我們通過這道題目為契機,希望考察的是候選人在分析問題解決問題的能力,在交流過程中所體現(xiàn)的邏輯推理和思維方式等,當(dāng)然也有最后實實在在的 code。問:這不是一道數(shù)學(xué)題目嗎,為什么程序員面試需要考察這樣的數(shù)學(xué)問題?
答:同上,不是考察這道題目本身。另外,這也可以說不是一道數(shù)學(xué)題目,當(dāng)然能用數(shù)學(xué)的方式解答。候選人能用數(shù)學(xué)的方式解答也算正確。問:二分是這道題目的標(biāo)準(zhǔn)答案嗎?我能用其他解法嗎?
答:同上,題目沒有標(biāo)準(zhǔn)答案,就算你用最暴力的算法搜索出來也是正確的解法,其他數(shù)學(xué)方法也對。問:這道題目這么簡單,牛頓迭代分分鐘秒掉,是不是太簡單了?
答: 給你點贊。
問:這題目在說什么,我搞了半天沒看懂,這TM是啥?
答:如果確實認(rèn)真看完整篇文章或跟面試官交流了那么久,還是根本不明白這到底說的是一個什么問題。那就別管了吧,隨他去吧,可能不是目標(biāo)用戶而已。問:我在實際工作中根本就不會遇到這樣的問題,你問這個有什么用?
答:同第2條答案。問:你們公司還缺人么,面試會考察哪些點?
答:有興趣或者有其他問題可以戳我郵箱,郵箱地址:aUB0YW5nbGVpLm1l。 面試考察可能會涉及:CS 基礎(chǔ)/Code/數(shù)據(jù)結(jié)構(gòu)和算法/解決問題/項目經(jīng)驗/系統(tǒng)設(shè)計/溝通團隊協(xié)作等等。
結(jié)語
本文題目是“從一道面試題談?wù)勔痪€大廠碼農(nóng)應(yīng)該具備的基本能力”,其實,上面大部分內(nèi)容只談到了這道題目本身(也穿插了一些對這道題目的分析和理解)。上述題目的場景是社招面試中的,對于這樣的題目來說校招的反饋會更好。因為在校生可能對于工作經(jīng)驗,項目經(jīng)驗等比較欠缺,所以只好用一些比較固定的算法來面試進行篩選(本質(zhì)上跟學(xué)校考試沒有太大的區(qū)別)。
但這種類似的題目在社招場景下就完全不適用嗎?社招的的同學(xué)寫不出來就有很充分的理由嗎?或許你在工作場景中不會遇到實際這種題目,但我其實想表達的是,作為在最前線寫代碼的碼農(nóng),在別人講解了二分算法且自己也能理解的情況下,能寫出這個二分算法應(yīng)該不算太難?相當(dāng)于一個需求,大家討論了算法實現(xiàn)和解法,需要你把它變成能跑的 code 而已。
其實這篇文章最開始叫“從一道面試題談?wù)勔痪€碼農(nóng)應(yīng)該具備的基本能力”,幾年前發(fā)出來被噴了,后來想想似乎被噴也有點道理,因為在日常有些場景下,“復(fù)制粘貼”工程師貌似也夠用了,遇到問題有更高水平的人來幫你解決就行,大家都一樣的話,怎么體現(xiàn)高手水平呢?但從用人單位角度想,當(dāng)然是更希望招聘更加優(yōu)秀的選手,怎樣體現(xiàn)優(yōu)秀呢?候選人基數(shù)太大,怎么篩選,其實也就“高考”一樣嘛,通過“考試”擇優(yōu)錄取而已。
我們就不去討論是否每個寫代碼的人都需要有這樣的能力(好像答案也是顯而易見并不是)。但我建議咱們一線的程序員們(特指有上進心的一線程序員)應(yīng)該對一些基本的數(shù)據(jù)結(jié)構(gòu)和算法有所了解,對常見的算法復(fù)雜度有所了解? 或者至少應(yīng)該有這樣的追求吧?比如二分搜索復(fù)雜度為什么是 。之前遇到過比如有的候選人,Java 開發(fā)七八年經(jīng)驗了,簡歷描述精通 Java,但不清楚
ArrayList, HashMap
內(nèi)部大概是怎么做的(我理解,不管什么都需要知道大致的實現(xiàn)原理才有可能去優(yōu)化遇到的各種性能問題吧?)。還有什么熟練掌握 Vim,結(jié)果其實就是熟練掌握如何打開和關(guān)閉 vim。還有的候選人口頭表達頭頭是道,結(jié)果落實到寫代碼就根本下不了筆。
有時候感覺大部分程序員都被大量的需求壓迫著,被產(chǎn)品經(jīng)理催促著,倉促地碼著繁瑣的業(yè)務(wù)代碼,不斷的改著 Bug 又引入新的 Bug。 業(yè)務(wù)代碼重要么,當(dāng)然重要(代碼就是服務(wù)于具體業(yè)務(wù)的),但同時也還是希望我們不要拋棄一些基礎(chǔ)的東西,多培養(yǎng)一下我們的編程素養(yǎng)。我們在用編程語言,利用各種工具來實現(xiàn)我們想要達到的目的的時候,能做到“知其然,知其所以然”豈不更好?
特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!