一些算法記錄

http://wt.guolairen.com/20110517/n552112.shtml

騰訊筆試題: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段,可以分的段數少一寫,這樣在掃描記數段時會快一些,還能節省一些內存)

引用3:還是給個僞代碼吧


  騰訊筆試題:兩個整數集合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只小白鼠。

更詳細解釋:http://blog.csdn.net/samuelltk/article/details/6901191

由題可知:由於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即可。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章