1. 程式人生 > >【面試專題】騰訊PHP實習生筆試題目及答案解析

【面試專題】騰訊PHP實習生筆試題目及答案解析

騰訊筆試題:const的含義及實現機制分析:

  const的含義及實現機制,比如:const int i,是怎麼做到i只可讀的?

  const用來說明所定義的變數是隻讀的。

  這些在編譯期間完成,編譯器可能使用常數直接替換掉對此變數的引用。

  初探編譯器static、const之實現原理

  騰訊筆試題:買200返100優惠券,實際上折扣是多少?

  到商店裡買200的商品返還100優惠券(可以在本商店代替現金)。請問實際上折扣是多少?

  分析:

  由於優惠券可以代替現金,所以可以使用200元優惠券買東西,然後還可以獲得100元的優惠券。

  假設開始時花了x元,那麼可以買到 x + x/2 + x/4 + …的東西。所以實際上折扣是50%.(當然,大部分時候很難一直兌換下去,所以50%是折扣的上限)

  如果使用優惠券買東西不能獲得新的優惠券,那麼

  總過花去了200元,可以買到200+100元的商品,所以實際折扣為 200/300 = 67%.

  騰訊筆試題:tcp三次握手的過程,accept發生在三次握手哪個階段?

  分析:

  accept發生在三次握手之後。

  第一次握手:客戶端傳送syn包(syn=j)到伺服器。

  第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個ASK包(ask=k)。

  第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1)。

  三次握手完成後,客戶端和伺服器就建立了tcp連線。這時可以呼叫accept函式獲得此連線。

  騰訊筆試題:用UDP協議通訊時怎樣得知目標機是否獲得了資料包

  用UDP協議通訊時怎樣得知目標機是否獲得了資料包?

  分析:

  可以在每個資料包中插入一個唯一的ID,比如timestamp或者遞增的int。

  傳送方在傳送資料時將此ID和傳送時間記錄在本地。

  接收方在收到資料後將ID再發給傳送方作為迴應。

  傳送方如果收到迴應,則知道接收方已經收到相應的資料包;如果在指定時間內沒有收到迴應,則資料包可能丟失,需要重複上面的過程重新發送一次,直到確定對方收到。

  騰訊筆試題:統計論壇線上人數分佈

  求一個論壇的線上人數,假設有一個論壇,其註冊ID有兩億個,每個ID從登陸到退出會向一個日誌檔案中記下登陸時間和退出時間,要求寫一個演算法統計一天中論壇的使用者線上分佈,取樣粒度為秒。

  分析:

  一天總共有 3600*24 = 86400秒。

  定義一個長度為86400的整數陣列int delta[86400],每個整數對應這一秒的人數變化值,可能為正也可能為負。開始時將陣列元素都初始化為0。

  然後依次讀入每個使用者的登入時間和退出時間,將與登入時間對應的整數值加1,將與退出時間對應的整數值減1。

  這樣處理一遍後陣列中儲存了每秒中的人數變化情況。

  定義另外一個長度為86400的整數陣列int online_num[86400],每個整數對應這一秒的論壇線上人數。

  假設一天開始時論壇線上人數為0,則第1秒的人數online_num[0] = delta[0]。第n+1秒的人數online_num[n] = online_num[n-1] + delta[n]。

  這樣我們就獲得了一天中任意時間的線上人數。

  騰訊筆試題:從10G個數中找到中數

  在一個檔案中有 10G 個整數,亂序排列,要求找出中位數。記憶體限制為 2G。

  分析:

  不妨假設10G個整數是64bit的。

  2G記憶體可以存放256M個64bit整數。

  我們可以將64bit的整數空間平均分成256M個取值範圍,用2G的記憶體對每個取值範圍內出現整數個數進行統計。這樣遍歷一邊10G整數後,我們便知道中數在那個範圍內出現,以及這個範圍內總共出現了多少個整數。

  如果中數所在範圍出現的整數比較少,我們就可以對這個範圍內的整數進行排序,找到中數。如果這個範圍內出現的整數比較多,我們還可以採用同樣的方法將此範圍再次分成多個更小的範圍(256M=2^28,所以最多需要3次就可以將此範圍縮小到1,也就找到了中數)。

詳細解釋:

網上給出的一個答案是採用桶的思想,把資料對映到很多桶,再取中間桶找中位數,整個過程下來要掃描兩遍資料。但其關鍵是對這海量的數大小有預先限制,那現在假設這些數的大小無法預先限制,而且10G個的整數,或者說題目改為每次只能去五分之一的資料拿到記憶體進行排序,那又該如何去解答這道題呢?

思考中。。。

附網上搜到的一個方法:

關於中位數:資料排序後,位置在最中間的數值。即將資料分成兩部分,一部分大於該數值,一部分小於該數值。中位數的位置:當樣本數為奇數時,中位數=(N+1)/2 ; 當樣本數為偶數時,中位數為N/2與1+N/2的均值(那麼10G個數的中位數,就第5G大的數與第5G+1大的數的均值了)。

分析:明顯是一道工程性很強的題目,和一般的查詢中位數的題目有幾點不同。
1. 原資料不能讀進記憶體,不然可以用快速選擇,如果數的範圍合適的話還可以考慮桶排序或者計數排序,但這裡假設是32位整數,仍有4G種取值,需要一個16G大小的陣列來計數。

2. 若看成從N個數中找出第K大的數,如果K個數可以讀進記憶體,可以利用最小或最大堆,但這裡K=N/2,有5G個數,仍然不能讀進記憶體。

3. 接上,對於N個數和K個數都不能一次讀進記憶體的情況,《程式設計之美》裡給出一個方案:設k<K,且k個數可以完全讀進記憶體,那麼先構建k個數的堆,先找出第0到k大的數,再掃描一遍陣列找出第k+1到2k的數,再掃描直到找出第K個數。雖然每次時間大約是nlog(k),但需要掃描ceil(K/k)次,這裡要掃描5次。

解法:首先假設是32位無符號整數。
1. 讀一遍10G個整數,把整數對映到256M個區段中,用一個64位無符號整數給每個相應區段記數。
說明:整數範圍是0 - 2^32 - 1,一共有4G種取值,對映到256M個區段,則每個區段有16(4G/256M = 16)種值,每16個值算一段, 0~15是第1段,16~31是第2段,……2^32-16 ~2^32-1是第256M段。一個64位無符號整數最大值是0~8G-1,這裡先不考慮溢位的情況。總共佔用記憶體256M×8B=2GB。

2. 從前到後對每一段的計數累加,當累加的和超過5G時停止,找出這個區段(即累加停止時達到的區段,也是中位數所在的區段)的數值範圍,設為[a,a+15],同時記錄累加到前一個區段的總數,設為m。然後,釋放除這個區段佔用的記憶體。

3. 再讀一遍10G個整數,把在[a,a+15]內的每個值計數,即有16個計數。

4. 對新的計數依次累加,每次的和設為n,當m+n的值超過5G時停止,此時的這個計數所對應的數就是中位數。

總結:
1.以上方法只要讀兩遍整數,對每個整數也只是常數時間的操作,總體來說是線性時間。

2. 考慮其他情況。
若是有符號的整數,只需改變對映即可。若是64為整數,則增加每個區段的範圍,那麼在第二次讀數時,要考慮更多的計數。若過某個計數溢位,那麼可認定所在的區段或代表整數為所求,這裡只需做好相應的處理。噢,忘了還要找第5G+1大的數了,相信有了以上的成果,找到這個數也不難了吧。

3. 時空權衡。
花費256M個區段也許只是恰好配合2GB的記憶體(其實也不是,呵呵)。可以增大區段範圍,減少區段數目,節省一些記憶體,雖然增加第二部分的對單個數值的計數,但第一部分對每個區段的計數加快了(總體改變??待測)。

4. 對映時儘量用位操作,由於每個區段的起點都是2的整數冪,對映起來也很方便。

引用2:http://hi.baidu.com/mcgrady32303/blog/item/5a61cd08691c29d03bc763c5.html 騰訊的一道面試題:(與百度相似,可惜昨天百度死在這方面了)//// 在一個檔案中有 10G 個整數,亂序排列,要求找出中位數。記憶體限制為 2G。只寫出思路即可。 答案: 1, 把整數分成256M段,每段可以用64位整數儲存該段資料個數,256M*8 = 2G記憶體,先清0 
2,讀10G整數,把整數對映到256M段中,增加相應段的記數 
3,掃描256M段的記數,找到中位數的段和中位數的段前面所有段的記數,可以把其他段的記憶體釋放 
4,因中位數段的可能整數取值已經比較小(如果是32bit整數,當然如果是64bit整數的話,可以再次分段),對每個整數做一個記數,再讀一次10G整數,只讀取中位數段對應的整數,並設定記數。 
5,對新的記數掃描一次,即可找到中位數。 
如果是32bit整數,讀10G整數2次,掃描256M記數一次,後一次記數因數量很小,可以忽略不記(設是32bit整數,按無符號整數處理 
整數分成256M段? 整數範圍是0 - 2^32 - 1 一共有4G種取值,4G/256M = 16,每16個數算一段 0-15是1段,16-31是一段,... 
整數對映到256M段中? 如果整數是0-15,則增加第一段記數,如果整數是16-31,則增加第二段記數,... 

其實可以不用分256M段,可以分的段數少一寫,這樣在掃描記數段時會快一些,還能節省一些記憶體)

  騰訊筆試題:兩個整數集合A和B,求其交集

  兩個整數集合A和B,求其交集。

  分析:

  1. 讀取整數集合A中的整數,將讀到的整數插入到map中,並將對應的值設為1。

  2. 讀取整數集合B中的整數,如果該整數在map中並且值為1,則將此數加入到交集當中,並將在map中的對應值改為2。

通過更改map中的值,避免了將同樣的值輸出兩次。

  騰訊筆試題:找出1到10w中沒有出現的兩個數字

  分析:

  有1到10w這10w個數,去除2個並打亂次序,如何找出那兩個數?

  申請10w個bit的空間,每個bit代表一個數字是否出現過。

  開始時將這10w個bit都初始化為0,表示所有數字都沒有出現過。

  然後依次讀入已經打亂循序的數字,並將對應的bit設為1。

  當處理完所有數字後,根據為0的bit得出沒有出現的數字。

  首先計算1到10w的和,平方和。

  然後計算給定數字的和,平方和。

  兩次的到的數字相減,可以得到這兩個數字的和,平方和。

  所以我們有

  x + y = n

  x^2 + y^2 = m

  解方程可以得到x和y的值。

  騰訊筆試題:需要多少隻小白鼠才能在24小時內找到毒藥

  有1000瓶水,其中有一瓶有毒,小白鼠只要嘗一點帶毒的水24小時後就會死亡,至少要多少隻小白鼠才能在24小時時鑑別出那瓶水有毒?

  分析:

  最容易想到的就是用1000只小白鼠,每隻喝一瓶。但顯然這不是最好答案。

  既然每隻小白鼠喝一瓶不是最好答案,那就應該每隻小白鼠喝多瓶。那每隻應該喝多少瓶呢?

  首先讓我們換種問法,如果有x只小白鼠,那麼24小時內可以從多少瓶水中找出那瓶有毒的?

  由於每隻小白鼠都只有死或者活這兩種結果,所以x只小白鼠最大可以表示2^x種結果。如果讓每種結果都對應到某瓶水有毒,那麼也就可以從2^x瓶水中找到有毒的那瓶水。那如何來實現這種對應關係呢?

  第一隻小白鼠喝第1到2^(x-1)瓶,第二隻小白鼠喝第1到第2^(x-2)和第2^(x-1)+1到第2^(x-1) + 2^(x-2)瓶….以此類推。

  回到此題,總過1000瓶水,所以需要最少10只小白鼠。

由題可知:由於20小時後才能知道小狗是否中毒,而且中毒的時間其實是無法精確到某一具體時間點或較短時間段的,所以每隔一段時間給狗吃藥,根據20小時後記錄小狗的發病時間來推算有毒藥水的方法被我直接否定。而且其他所有拿細分時間段做文章的解題思路也一併摒棄。

因為那些方法除了可操作性幾乎沒有外,還有一種必然出局的理由就是每個人都會想到它,這就無法達到出題者測試應聘者能力的目的。因此,我覺得固守時間段思路的應聘者很難不被淘汰。

確定了思考角度,我所面臨的難點就是如何將10只小狗和1000瓶藥水建立某種聯絡,而這種聯絡還要受24-20=4小時的制約。

我所能想到的是將藥水以某種形式交叉混合,然後餵給小狗,最終通過分析中毒小狗的數量和次序來確定有毒藥水。

但是思前想後,最終我還是沒能理出具體頭緒,無奈只好求助於百度了。

找到題目出處很簡單,當我看到原帖第6樓網友“OO”回覆的答案後,我徹底拜服了,也自此絕了進騰訊等大公司的念想。

-=================================================================-

下面請看網友“OO”的解法:

把狗從0-9編號;

把藥水按1-1000編號;

把藥水編號按二進位制,如果第i位(因為最大1000,所以bit位為0-9)bit位為1,則分給編號為i的狗狗喝;

最後得一二進位制數,如果編號為i的狗狗死了,該數的第i bit位為1,該數就是有毒的藥水編號。

他說的比較專業,下面我用例項給解析一下:

用 0、1、2、3、4、5、6、7、8、9 給小狗編號;

而藥水按1-1000編號;

我們把每瓶藥水的編號轉換為二進位制數,由於2的10次方=1024,所以我們將二進位制數定為有10個數位,如:

1=0000000001

13=0000001101

214=0011010110

對二進位制轉換不熟悉的朋友可以用“開始-程式-附件-計算器-檢視-科學型”來輕鬆轉換。

這樣轉換以後,每個藥水編號的二進位制數的每一位都分別對應一隻小狗;

我們定義每瓶藥水要餵給其二進位制編號位數為“1”的那位對應的小狗喝;

由於2的10次方=1024>1000,所以這些二進位制編號組合都是唯一的;

當我們用不到4小時的時間將1000瓶藥水分別餵給相應的小狗喝後,就可以去看看書,上上網,聽聽歌來打發剩下的20小時;

20小時候,在一個合理的藥效發作時間後,我們統計有中毒症狀小狗的編號,中毒的定為“1”,正常的定為“0”;

然後依照編號順序排列,我們就可以得到一個10位的二進位制數,而將這個二進位制數再轉換為十進位制數後,這個數值就是有毒的藥水的編號了;

例如,最終結果是編號為 2、4、6、7、9 的小狗有中毒症狀,我們就將一個十位二進位制數的2、4、6、7、9位設為“1”,其餘各位設為“0”,即:0010101101;

而0010101101對應的十進位制數=173,所以第173瓶藥水就是有毒藥水!

當然,如果都無毒,10只小狗就會都是活蹦亂跳的。

-=================================================================-

相信通過上面的解釋,大家基本上都能理解這個解題思想的過程了吧。

回過頭來反思為什麼老王沒能想出這麼具體的解法呢?

最主要的一點還是思想不夠活,沒能將早就學過的計算機知識應用到實際解題過程中,這也就決定了我和OO等高手之間的差距。

介紹這種解題方法當然不等於沒有其他更好的解法,只是我覺得這種解法就是出題者想要達成的目的。

而且在我看來,其他類似時間分塊方法的可操作性和實現的科學性,照這個方法也確實是差的太多。

不過有一種現象很有趣,在原帖中,OO在第6樓就釋出了他的解法,但下面還有數十上百個回帖在積極地發表和探討各種漏洞百出的解法。看來人的思維的確很複雜,創新思想和求勝心理使很多人懷疑一個已經很不錯的結論。不過我倒覺得適時肯定別人的成果並消化掉對自己的經驗積累也很有用。


  騰訊筆試題:根據上排的數填寫下排的數,並滿足要求。

  根據上排給出十個數,在其下排填出對應的十個數, 要求下排每個數都是上排對應位置的數在下排出現的次數。上排的數:0,1,2,3,4,5,6,7,8,9。

  騰訊筆試題:判斷數字是否出現在40億個數中?

  給40億個不重複的unsigned int的整數,沒排過序的,然後再給幾個數,如何快速判斷這幾個數是否在那40億個數當中?

  分析:

  unsigned int 的取值範圍是0到2^32-1。我們可以申請連續的2^32/8=512M的記憶體,用每一個bit對應一個unsigned int數字。首先將512M記憶體都初始化為0,然後每處理一個數字就將其對應的bit設定為1。當需要查詢時,直接找到對應bit,看其值是0還是1即可。