算法实习生面试记录
算法實習生面試
2018/5/21????前記: 現在大三,想找一份暑期實習生工作。陸陸續續面了幾家公司,基本定位都在算法方向。由于前期沒有準備,所以基本結果都很慘。第一次去面試時,直接被面試官現教。現在實習基本確定,所以想要整理一下這段時間的面試經過,特別是針對一些被問到的面試題統一規整,希望為之后的找工作留下經驗,也分享出來供大家參考。
一. 面試題目
? 挑選我認為比較具有代表性的題目來分享,也是重新整理,給自己加深印象。
1.【頭條】給出一個計算x\sqrt xx? 的算法,并手寫代碼。
????只要學過計算機語言的同學應該都有做過這道題。這是一道非常基礎且典型的迭代算法題。基本思想是用牛頓迭代法。
至于什么是牛頓迭代法,可以參考這里
????牛頓迭代法常被用來作方程的尋根,這里最重要的就是把計算開根號的運算轉化為方程尋根問題。公式如下:
x2?a=0x^2-a=0x2?a=0
現在問題就變成了:已知一個非負數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)計算f(xn)f(x_n)f(xn?),如果∣f(x)∣<?|f(x)|<\epsilon∣f(x)∣<?,則停止迭代,?\epsilon?為給定的誤差項,否則轉到3);
3)計算: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)。
到此牛頓迭代法就完成了。
????我面試的時候,知道要將這個問題轉換,因為不熟練愣是沒有想起怎么轉為方程求根。當時我索性用了二分的辦法來計算,不過面試官似乎覺得也還能接受。這個經驗告訴我學習時一定不要眼高手低,對于這種小細節也要同等關注,細節決定成敗不是沒有道理的。
2.【頭條】題目:有一個根據日期排列,內容為當天天氣溫度的列表A。請你根據該列表A,返回一個同樣大小的列表B,滿足列表B中每一個位置的值為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]
????最容易想到的辦法,就是對A中的每一個元素按從前往后的順序去遍歷其后的溫度,只要找到比它大的值,就可以停止。時間復雜度為O(n2)O(n^2)O(n2),顯然這不是最好的辦法。
下面給出一種時間復雜度可以達到O(n)O(n)O(n)的算法。
假設A共有n個元素,對列表A按照從后往前的順序每一個進行遍歷:
-
顯然Bn?1=?1B_{n-1}=-1Bn?1?=?1;
-
對于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?說明對于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,更為重要的是,對于索引小于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?已經沒有比較的意義,因為An?2A_{n-2}An?2?比An?1A_{n-1}An?1?離這些元素更近,且An?1不會比An?2大A_{n-1}不會比A_{n-2}大An?1?不會比An?2?大,此時我們可以在列表A中刪去An?1A_{n-1}An?1?, 這樣可以節省前面的元素遍歷時的時間復雜度。
-
對于i=n?3,n?4,...,0i=n-3,n-4,...,0i=n?3,n?4,...,0, 都是逐個與后面的值進行比較,直到找到比AiA_iAi?大的值,但由于每次遍歷都存在刪除上的策略,所以可以大大降低時間復雜度,對于每一個AiA_iAi?,遍歷所消耗的時間復雜度為O(1)O(1)O(1),整體復雜度為O(n)O(n)O(n)。
????在面試官的提示下,我開始手寫代碼。當時直接用了數組來做,但其實會遇到一個增加時間復雜度的問題,就是刪除節點時會產生O(n)O(n)O(n)的時間復雜度。后面發現其實更好的策略是用c++中的鏈表來做,這樣在結構體中同時包含當前天氣的日期以及溫度,并且刪除節點的時間復雜度為O(1)O(1)O(1),大大提高了效率,并且時刻都能保證日期與溫度的一一對應。
補上代碼【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.【頭條】題目:有一個列表,每個元素都是一個大于等于0的整數,請計算出符合一下條件的最大的hhh: 列表中至少有h個元素的值大于等于h。h。h。例如,列表為[1,2,6,3,7][1,2,6,3,7][1,2,6,3,7],最大的hhh應為3。
????這個問題比較繞,需要先看懂題意,尤其要注意條件中的“至少”,“大于等于”。
直接給出算法,主要是三部分:
- 令新列表C長度為n+1n+1n+1,CiC_iCi?表示B中值為i的元素的個數,其中CnC_nCn?表示B中大于等于n的元素的個數。(因為hhh的最大值為nnn,所以對于B中大于nnn的值不必再在C中為其設立對應的位置,這樣可以節省不必要的內存開銷。)建立列表C的時間復雜度為O(n)O(n)O(n);
- 在列表C中從后往前判斷hhh的值,直到找到滿足條件的hhh,即為最大的hhh。
????整個算法的時間復雜度為O(n)O(n)O(n)。
補上代碼【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.【百度】利用堆排序找到一個列表中第kkk大的數。
????看網上已有的算法面經,似乎很多面試官都喜歡考堆排序算法。所以以后再想要去面算法工程師,一定要記得復習堆排序算法。其實不僅僅是堆排序,作為算法中經典的一類問題,排序算法始終都會成為考查的熱點。比如快速排序,歸并排序,冒泡排序,都是作為一個算法開發人員必備的基本技能點。
????下面簡單介紹堆排序算法。
堆排序是一種不穩定的選擇排序算法,時間復雜度為O(nlogn)O(nlogn)O(nlogn)。
堆排序是依托于堆結構而存在的,根據節點的關系分為大頂堆和小頂堆。
大頂堆:每個節點的值大于等于其左右子節點的值。
小頂堆:每個節點的值小于等于其左右子節點的值。
堆在邏輯上是一種完全二叉樹結構,但是我們在內存中用數組來存放(根據樹從上層到下層,從左到右進行排放)。
用一個數組來定義堆:
堆中最后一個非葉節點的索引:(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];
堆排序基本步驟:
-
將無序數列構成一個堆,按升、降序選擇大頂堆、小頂堆;
數組中最后一個非葉節點的索引:[arr.length/2]?1[arr.length/2]-1[arr.length/2]?1,從右向左,從下至上調整。 -
堆頂元素為最大(最小)元素,將堆頂與最后一個元素交換,將堆頂元素沉入數組末端;
-
重新調整數組,使其成為堆,再重復步驟2,直至排序結束。
補上代碼【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.【阿里】有一個文本文件,共包含n行,每一行為一個浮點數,設計算法計算該文本中所有浮點數的均值和方差。
乍一看,這個題是不是很簡單。我面試的時候看到題目也覺得很不可思議,不過在應試教育中掙扎多年的我,知道這個問題不簡單,里面可能有坑。事實證明的確如此。
1)算法一:我一開始想到的算法是,遍歷一次文本,并將其內容存入一個數組,在遍歷的同時計算均值這并不困難。在完成一次遍歷后即可得到均值,再根據已經存下來的數組,利用方差公式σ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計算方差即可。所以時間復雜度為O(n)O(n)O(n),空間復雜度也為O(n)O(n)O(n)。
2)算法二:簡化算法,使得空間復雜度為O(1)O(1)O(1)。在算法一中,我們一次遍歷,由于將內容村委了一個數組用來計算方差,所以消耗了O(n)O(n)O(n)的空間復雜度。現在我們想要空間復雜度降低,其實很簡單, 我們在第一次遍歷的時候先計算出平均值,但是不保存數組。然后進行第二次遍歷,依舊去讀文本內容,根據第一次遍歷得出的均值計算方差。這樣時間復雜度仍然為O(n)O(n)O(n),但空間復雜度降低為O(1)O(1)O(1),弊端在于我們需要遍歷兩次文本。
3)算法三:算法二雖然可以達到O(1)O(1)O(1)的空間復雜度,但是需要遍歷兩次文本。進一步優化算法,希望只需要遍歷一次文本,并且空間復雜度仍為O(1)O(1)O(1),時間復雜度仍為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。
這樣就可以在時間復雜度為O(n)O(n)O(n),空間復雜度為O(1)O(1)O(1),且遍歷一次文本就可以得到均值和方差。
至此,這個問題基本可以達到很好的解決了。
二. 一點經驗
????一些大廠,雖然他們的招聘要求都寫得玄而又玄,看起來很厲害的樣子。實際上,對于實習生,特別是年級較低的實習生,他們更看重的是你的基礎能力。所謂的基礎能力,包括編程能力(程序員基本功),編程再不濟c++要會吧,而且要熟練的會吧;另外就是數學能力,特別是做算法崗,數學能力也是有必要的,如果有志向做機器學習、大數據分析等方向,那數學肯定是要過關的,最基本的函數求偏導,極限肯定是要會的。前面說的的基本能力,作為一個想要找實習的學生,想要達到并不難。一個崗位往往有很多人去競爭,一群能力差不多的人,憑什么選你呢。這時你需要有一個閃光的地方,讓人家覺得你能給人家帶來更亮眼的東西,比如你的編程能力很厲害,所謂厲害不是說對于面試官給你提出的問題你都能輕松的寫出可執行代碼。你需要有更多的大項目編程經歷,比如一個很厲害的競賽,面試官很看重這一點。或者說你的數學能力很厲害,你有著強悍的數學功底,對于一些AI方向的職位,可能會更受歡迎。
寫在最后: 第一次找實習,也比較看重這次實習。從來沒有走出過校門的學生黨,走出去發現外面的世界還是很不一樣的。企業里并不會在乎你有多大的發展空間,他們在乎的是你能否給他們創造利益。特別的,一些大廠,比如京東,他們大規模的招聘實習生,其中一個很重要的目的是為下一年的校招做準備,所以同等能力下,像我這種只是純粹為了去實習的升學黨就處于相對的弱勢,因為招我們進去,更像是培養我們個人,對企業來講得不到太長遠的好處。 另外一點,就是比較后悔前兩年沒有出來實習。現在找實習發現編程、數據結構、算法什么的都是大一就學過的東西,那時候覺得自己學的都是些什么啊,拿到外面肯定都沒用。現在才發現,其實大一大二完全有能力出來實習了,只不過那時候意識不到。所以趁著低年級假期沒事干出來實習真的很有必要。
最后推薦幾個個人認為比較好的IT刷題網站:牛客網(算法),Leetcode(算法,數據競賽),Kuggle(數據競賽)。
?
總結
- 上一篇: 给你的web页面添加盲水印,附带检盲水印
- 下一篇: VMware Workstation下载