算法实习生面试记录
算法實(shí)習(xí)生面試
2018/5/21????前記: 現(xiàn)在大三,想找一份暑期實(shí)習(xí)生工作。陸陸續(xù)續(xù)面了幾家公司,基本定位都在算法方向。由于前期沒有準(zhǔn)備,所以基本結(jié)果都很慘。第一次去面試時(shí),直接被面試官現(xiàn)教。現(xiàn)在實(shí)習(xí)基本確定,所以想要整理一下這段時(shí)間的面試經(jīng)過,特別是針對(duì)一些被問到的面試題統(tǒng)一規(guī)整,希望為之后的找工作留下經(jīng)驗(yàn),也分享出來供大家參考。
一. 面試題目
? 挑選我認(rèn)為比較具有代表性的題目來分享,也是重新整理,給自己加深印象。
1.【頭條】給出一個(gè)計(jì)算x\sqrt xx? 的算法,并手寫代碼。
????只要學(xué)過計(jì)算機(jī)語言的同學(xué)應(yīng)該都有做過這道題。這是一道非常基礎(chǔ)且典型的迭代算法題。基本思想是用牛頓迭代法。
至于什么是牛頓迭代法,可以參考這里
????牛頓迭代法常被用來作方程的尋根,這里最重要的就是把計(jì)算開根號(hào)的運(yùn)算轉(zhuǎn)化為方程尋根問題。公式如下:
x2?a=0x^2-a=0x2?a=0
現(xiàn)在問題就變成了:已知一個(gè)非負(fù)數(shù)a,求出方程f(x)=0f(x)=0f(x)=0的根,其中f(x)=x2?af(x)=x^2-af(x)=x2?a。
牛頓法的迭代公式如下:xn+1=xn?f′(xn)f(xn)x_{n+1}=x_n- \frac {f'(x_n)} {f(x_n)}xn+1?=xn??f(xn?)f′(xn?)?
具體迭代步驟如下:
1)n=0,x0n=0,x_0n=0,x0?為初始值,可以選擇x0=1x_0=1x0?=1;
2)計(jì)算f(xn)f(x_n)f(xn?),如果∣f(x)∣<?|f(x)|<\epsilon∣f(x)∣<?,則停止迭代,?\epsilon?為給定的誤差項(xiàng),否則轉(zhuǎn)到3);
3)計(jì)算:xn+1=xn?f′(xn)f(xn)x_{n+1}=x_n- \frac {f'(x_n)} {f(x_n)}xn+1?=xn??f(xn?)f′(xn?)?,回到2)。
到此牛頓迭代法就完成了。
????我面試的時(shí)候,知道要將這個(gè)問題轉(zhuǎn)換,因?yàn)椴皇炀氥妒菦]有想起怎么轉(zhuǎn)為方程求根。當(dāng)時(shí)我索性用了二分的辦法來計(jì)算,不過面試官似乎覺得也還能接受。這個(gè)經(jīng)驗(yàn)告訴我學(xué)習(xí)時(shí)一定不要眼高手低,對(duì)于這種小細(xì)節(jié)也要同等關(guān)注,細(xì)節(jié)決定成敗不是沒有道理的。
2.【頭條】題目:有一個(gè)根據(jù)日期排列,內(nèi)容為當(dāng)天天氣溫度的列表A。請(qǐng)你根據(jù)該列表A,返回一個(gè)同樣大小的列表B,滿足列表B中每一個(gè)位置的值為A中該天之后距離其最近且比其溫度高的日期,若沒有則該位置為-1。例如,用索引代表日期,列表A=[23,22,15,14,17,23]A=[23,22,15,14,17,23]A=[23,22,15,14,17,23],則需要返回的列表為B=[?1,5,4,4,5,?1]B=[-1,5,4,4,5,-1]B=[?1,5,4,4,5,?1]
????最容易想到的辦法,就是對(duì)A中的每一個(gè)元素按從前往后的順序去遍歷其后的溫度,只要找到比它大的值,就可以停止。時(shí)間復(fù)雜度為O(n2)O(n^2)O(n2),顯然這不是最好的辦法。
下面給出一種時(shí)間復(fù)雜度可以達(dá)到O(n)O(n)O(n)的算法。
假設(shè)A共有n個(gè)元素,對(duì)列表A按照從后往前的順序每一個(gè)進(jìn)行遍歷:
-
顯然Bn?1=?1B_{n-1}=-1Bn?1?=?1;
-
對(duì)于An?2A_{n-2}An?2?,將其與An?1A_{n-1}An?1?比較:
- 如果An?1>An?2A_{n-1}>A_{n-2}An?1?>An?2?說明對(duì)于Bn?2=n?1B_{n-2}=n-1Bn?2?=n?1;
- 如果An?1≤An?2A_{n-1}\leq A_{n-2}An?1?≤An?2?,則Bn?2=?1,B_{n-2}=-1,Bn?2?=?1,更為重要的是,對(duì)于索引小于n-2的元素Ai,(i=n?3,n?4...,0)A_i,(i=n-3,n-4...,0)Ai?,(i=n?3,n?4...,0),An?1A_{n-1}An?1?已經(jīng)沒有比較的意義,因?yàn)?span id="ozvdkddzhkzd" class="katex--inline">An?2A_{n-2}An?2?比An?1A_{n-1}An?1?離這些元素更近,且An?1不會(huì)比An?2大A_{n-1}不會(huì)比A_{n-2}大An?1?不會(huì)比An?2?大,此時(shí)我們可以在列表A中刪去An?1A_{n-1}An?1?, 這樣可以節(jié)省前面的元素遍歷時(shí)的時(shí)間復(fù)雜度。
-
對(duì)于i=n?3,n?4,...,0i=n-3,n-4,...,0i=n?3,n?4,...,0, 都是逐個(gè)與后面的值進(jìn)行比較,直到找到比AiA_iAi?大的值,但由于每次遍歷都存在刪除上的策略,所以可以大大降低時(shí)間復(fù)雜度,對(duì)于每一個(gè)AiA_iAi?,遍歷所消耗的時(shí)間復(fù)雜度為O(1)O(1)O(1),整體復(fù)雜度為O(n)O(n)O(n)。
????在面試官的提示下,我開始手寫代碼。當(dāng)時(shí)直接用了數(shù)組來做,但其實(shí)會(huì)遇到一個(gè)增加時(shí)間復(fù)雜度的問題,就是刪除節(jié)點(diǎn)時(shí)會(huì)產(chǎn)生O(n)O(n)O(n)的時(shí)間復(fù)雜度。后面發(fā)現(xiàn)其實(shí)更好的策略是用c++中的鏈表來做,這樣在結(jié)構(gòu)體中同時(shí)包含當(dāng)前天氣的日期以及溫度,并且刪除節(jié)點(diǎn)的時(shí)間復(fù)雜度為O(1)O(1)O(1),大大提高了效率,并且時(shí)刻都能保證日期與溫度的一一對(duì)應(yīng)。
補(bǔ)上代碼【Python 3】
def weather(A):n = len(A)B = A.copy()C = [i for i in range(n)]D = [-1 for i in range(n)]for i in range(n-1,-1,-1):k = i+1tmp = len(B)for j in range(i+1,tmp):if(B[k]>B[i]):D[i] = C[k]breakelse:del B[k]del C[k]return Dif __name__=="__main__":A = [23,22,15,14,17,23]B = weather(A)print(B);3.【頭條】題目:有一個(gè)列表,每個(gè)元素都是一個(gè)大于等于0的整數(shù),請(qǐng)計(jì)算出符合一下條件的最大的hhh: 列表中至少有h個(gè)元素的值大于等于h。h。h。例如,列表為[1,2,6,3,7][1,2,6,3,7][1,2,6,3,7],最大的hhh應(yīng)為3。
????這個(gè)問題比較繞,需要先看懂題意,尤其要注意條件中的“至少”,“大于等于”。
直接給出算法,主要是三部分:
- 令新列表C長度為n+1n+1n+1,CiC_iCi?表示B中值為i的元素的個(gè)數(shù),其中CnC_nCn?表示B中大于等于n的元素的個(gè)數(shù)。(因?yàn)?span id="ozvdkddzhkzd" class="katex--inline">hhh的最大值為nnn,所以對(duì)于B中大于nnn的值不必再在C中為其設(shè)立對(duì)應(yīng)的位置,這樣可以節(jié)省不必要的內(nèi)存開銷。)建立列表C的時(shí)間復(fù)雜度為O(n)O(n)O(n);
- 在列表C中從后往前判斷hhh的值,直到找到滿足條件的hhh,即為最大的hhh。
????整個(gè)算法的時(shí)間復(fù)雜度為O(n)O(n)O(n)。
補(bǔ)上代碼【Python3】
def largest_h(data):n = len(data)index = [0 for i in range(n+1)]for i in range(n):index[min(n,data[i])] += 1for i in range(n,-1,-1):if(i==n and index[i]>=n):return iif(i<n):index[i]+=index[i+1]if(index[i]>=i):return iif __name__=="__main__":data = [1,2,6,3,7,5,4]#data = [5,5,5,5,5]print(largest_h(data))4.【百度】利用堆排序找到一個(gè)列表中第kkk大的數(shù)。
????看網(wǎng)上已有的算法面經(jīng),似乎很多面試官都喜歡考堆排序算法。所以以后再想要去面算法工程師,一定要記得復(fù)習(xí)堆排序算法。其實(shí)不僅僅是堆排序,作為算法中經(jīng)典的一類問題,排序算法始終都會(huì)成為考查的熱點(diǎn)。比如快速排序,歸并排序,冒泡排序,都是作為一個(gè)算法開發(fā)人員必備的基本技能點(diǎn)。
????下面簡單介紹堆排序算法。
堆排序是一種不穩(wěn)定的選擇排序算法,時(shí)間復(fù)雜度為O(nlogn)O(nlogn)O(nlogn)。
堆排序是依托于堆結(jié)構(gòu)而存在的,根據(jù)節(jié)點(diǎn)的關(guān)系分為大頂堆和小頂堆。
大頂堆:每個(gè)節(jié)點(diǎn)的值大于等于其左右子節(jié)點(diǎn)的值。
小頂堆:每個(gè)節(jié)點(diǎn)的值小于等于其左右子節(jié)點(diǎn)的值。
堆在邏輯上是一種完全二叉樹結(jié)構(gòu),但是我們?cè)趦?nèi)存中用數(shù)組來存放(根據(jù)樹從上層到下層,從左到右進(jìn)行排放)。
用一個(gè)數(shù)組來定義堆:
堆中最后一個(gè)非葉節(jié)點(diǎn)的索引:(n?1)/2(n-1)/2(n?1)/2;
大頂堆:arr[i]≥arr[2i+1]arr[i] \geq arr[2i+1]arr[i]≥arr[2i+1] && arr[i]≥arr[2i+2]arr[i]\geq arr[2i+2]arr[i]≥arr[2i+2];
小頂堆:arr[i]≤arr[2i+1]arr[i] \leq arr[2i+1]arr[i]≤arr[2i+1] && arr[i]≤arr[2i+2]arr[i]\leq arr[2i+2]arr[i]≤arr[2i+2];
堆排序基本步驟:
-
將無序數(shù)列構(gòu)成一個(gè)堆,按升、降序選擇大頂堆、小頂堆;
數(shù)組中最后一個(gè)非葉節(jié)點(diǎn)的索引:[arr.length/2]?1[arr.length/2]-1[arr.length/2]?1,從右向左,從下至上調(diào)整。 -
堆頂元素為最大(最小)元素,將堆頂與最后一個(gè)元素交換,將堆頂元素沉入數(shù)組末端;
-
重新調(diào)整數(shù)組,使其成為堆,再重復(fù)步驟2,直至排序結(jié)束。
補(bǔ)上代碼【Python3, 升序排序】
def lowfilter(data, i, n):while(i<n):lindex = i*2+1rindex = i*2+2if (rindex < n):if(data[rindex]>data[i] and data[rindex]>=data[lindex]):data[i],data[rindex] = data[rindex],data[i]i = rindexelif(data[lindex]>data[i] and data[lindex]>=data[rindex]):data[i],data[lindex] = data[lindex],data[i]i = lindexelse:i = nelif(lindex<n):if(data[lindex]>data[i]):data[lindex],data[i] = data[i],data[lindex]i = lindexelse:i = nelse:i = nreturn datadef crestack(data):n= len(data)for i in range((n>>1)-1, -1, -1):data = lowfilter(data, i, n)return datadef stacksort(data):n = len(data)data = crestack(data)for i in range(n):data[n-i-1],data[0] = data[0],data[n-i-1]data = lowfilter(data,0,n-i-1)return dataif __name__=="__main__":data = [6,4,1,7,0,-1]sortdata = stacksort(data.copy())print(data)print(sortdata)5.【阿里】有一個(gè)文本文件,共包含n行,每一行為一個(gè)浮點(diǎn)數(shù),設(shè)計(jì)算法計(jì)算該文本中所有浮點(diǎn)數(shù)的均值和方差。
乍一看,這個(gè)題是不是很簡單。我面試的時(shí)候看到題目也覺得很不可思議,不過在應(yīng)試教育中掙扎多年的我,知道這個(gè)問題不簡單,里面可能有坑。事實(shí)證明的確如此。
1)算法一:我一開始想到的算法是,遍歷一次文本,并將其內(nèi)容存入一個(gè)數(shù)組,在遍歷的同時(shí)計(jì)算均值這并不困難。在完成一次遍歷后即可得到均值,再根據(jù)已經(jīng)存下來的數(shù)組,利用方差公式σ2=1n∑i=1n(xi?x ̄)2\sigma ^2=\frac 1 n\sum_{i=1}^n(x_i-\overline x)^2σ2=n1?∑i=1n?(xi??x)2計(jì)算方差即可。所以時(shí)間復(fù)雜度為O(n)O(n)O(n),空間復(fù)雜度也為O(n)O(n)O(n)。
2)算法二:簡化算法,使得空間復(fù)雜度為O(1)O(1)O(1)。在算法一中,我們一次遍歷,由于將內(nèi)容村委了一個(gè)數(shù)組用來計(jì)算方差,所以消耗了O(n)O(n)O(n)的空間復(fù)雜度。現(xiàn)在我們想要空間復(fù)雜度降低,其實(shí)很簡單, 我們?cè)诘谝淮伪闅v的時(shí)候先計(jì)算出平均值,但是不保存數(shù)組。然后進(jìn)行第二次遍歷,依舊去讀文本內(nèi)容,根據(jù)第一次遍歷得出的均值計(jì)算方差。這樣時(shí)間復(fù)雜度仍然為O(n)O(n)O(n),但空間復(fù)雜度降低為O(1)O(1)O(1),弊端在于我們需要遍歷兩次文本。
3)算法三:算法二雖然可以達(dá)到O(1)O(1)O(1)的空間復(fù)雜度,但是需要遍歷兩次文本。進(jìn)一步優(yōu)化算法,希望只需要遍歷一次文本,并且空間復(fù)雜度仍為O(1)O(1)O(1),時(shí)間復(fù)雜度仍為O(n)O(n)O(n)。將方差公式展開:
σ2=1n∑i=1n(xi?x ̄)2=1n∑i=1n(xi2+x ̄2?2xix ̄)=1n∑i=1nxi2+x ̄2?2nx ̄∑i=1nxi\sigma ^2=\frac 1 n \sum_{i=1}^n(x_i-\overline x)^2 \\ \qquad \qquad \quad =\frac 1 n \sum_{i=1}^n(x_i^2+\overline x^2-2x_i\overline x)\\ \qquad \qquad \qquad =\frac 1 n \sum_{i=1}^nx_i^2+\overline x^2-\frac 2 n \overline x \sum_{i=1}^nx_iσ2=n1?i=1∑n?(xi??x)2=n1?i=1∑n?(xi2?+x2?2xi?x)=n1?i=1∑n?xi2?+x2?n2?xi=1∑n?xi?
公式展開到這里,就可以看明白了。一次遍歷中,累積xi2,xix_i^2, x_ixi2?,xi? 這樣一次遍歷后就可以一次得到均值x ̄\overline xx和方差σ2\sigma^2σ2。
這樣就可以在時(shí)間復(fù)雜度為O(n)O(n)O(n),空間復(fù)雜度為O(1)O(1)O(1),且遍歷一次文本就可以得到均值和方差。
至此,這個(gè)問題基本可以達(dá)到很好的解決了。
二. 一點(diǎn)經(jīng)驗(yàn)
????一些大廠,雖然他們的招聘要求都寫得玄而又玄,看起來很厲害的樣子。實(shí)際上,對(duì)于實(shí)習(xí)生,特別是年級(jí)較低的實(shí)習(xí)生,他們更看重的是你的基礎(chǔ)能力。所謂的基礎(chǔ)能力,包括編程能力(程序員基本功),編程再不濟(jì)c++要會(huì)吧,而且要熟練的會(huì)吧;另外就是數(shù)學(xué)能力,特別是做算法崗,數(shù)學(xué)能力也是有必要的,如果有志向做機(jī)器學(xué)習(xí)、大數(shù)據(jù)分析等方向,那數(shù)學(xué)肯定是要過關(guān)的,最基本的函數(shù)求偏導(dǎo),極限肯定是要會(huì)的。前面說的的基本能力,作為一個(gè)想要找實(shí)習(xí)的學(xué)生,想要達(dá)到并不難。一個(gè)崗位往往有很多人去競爭,一群能力差不多的人,憑什么選你呢。這時(shí)你需要有一個(gè)閃光的地方,讓人家覺得你能給人家?guī)砀裂鄣臇|西,比如你的編程能力很厲害,所謂厲害不是說對(duì)于面試官給你提出的問題你都能輕松的寫出可執(zhí)行代碼。你需要有更多的大項(xiàng)目編程經(jīng)歷,比如一個(gè)很厲害的競賽,面試官很看重這一點(diǎn)。或者說你的數(shù)學(xué)能力很厲害,你有著強(qiáng)悍的數(shù)學(xué)功底,對(duì)于一些AI方向的職位,可能會(huì)更受歡迎。
寫在最后: 第一次找實(shí)習(xí),也比較看重這次實(shí)習(xí)。從來沒有走出過校門的學(xué)生黨,走出去發(fā)現(xiàn)外面的世界還是很不一樣的。企業(yè)里并不會(huì)在乎你有多大的發(fā)展空間,他們?cè)诤醯氖悄隳芊窠o他們創(chuàng)造利益。特別的,一些大廠,比如京東,他們大規(guī)模的招聘實(shí)習(xí)生,其中一個(gè)很重要的目的是為下一年的校招做準(zhǔn)備,所以同等能力下,像我這種只是純粹為了去實(shí)習(xí)的升學(xué)黨就處于相對(duì)的弱勢(shì),因?yàn)檎形覀冞M(jìn)去,更像是培養(yǎng)我們個(gè)人,對(duì)企業(yè)來講得不到太長遠(yuǎn)的好處。 另外一點(diǎn),就是比較后悔前兩年沒有出來實(shí)習(xí)。現(xiàn)在找實(shí)習(xí)發(fā)現(xiàn)編程、數(shù)據(jù)結(jié)構(gòu)、算法什么的都是大一就學(xué)過的東西,那時(shí)候覺得自己學(xué)的都是些什么啊,拿到外面肯定都沒用。現(xiàn)在才發(fā)現(xiàn),其實(shí)大一大二完全有能力出來實(shí)習(xí)了,只不過那時(shí)候意識(shí)不到。所以趁著低年級(jí)假期沒事干出來實(shí)習(xí)真的很有必要。
最后推薦幾個(gè)個(gè)人認(rèn)為比較好的IT刷題網(wǎng)站:牛客網(wǎng)(算法),Leetcode(算法,數(shù)據(jù)競賽),Kuggle(數(shù)據(jù)競賽)。
?
總結(jié)
- 上一篇: 给你的web页面添加盲水印,附带检盲水印
- 下一篇: VMware Workstation下载