面試常用算法


1、給定a、b兩個文件,各存放50億個url,每個url各占64字節,內存限制是4G,讓你找出a、b文件共同的url?
1)   可以估計每個文件安的大小為50G×64=320G,遠遠大於內存限制的4G。所以不可能將其完全加載到內存中處理。考慮采取分而治之的方法。
2)   遍歷文件a,對每個url求取 ,然后根據所取得的值將url分別存儲到1000個小文件(記為 )中。這樣每個小文件的大約為300M。
3)   遍歷文件b,采取和a相同的方式將url分別存儲到1000個小文件(記為 )。這樣處理后,所有可能相同的url都在對應的小文件( )中,不對應的小文件不可能有相同的url。然后我們只要求出1000對小文件中相同的url即可
4)   求每對小文件中相同的url時,可以把其中一個小文件的url存儲到hash_set中。然后遍歷另一個小文件的每個url,看其是否在剛才構建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
 
 
 2、有10個文件,每個文件1G,每個文件的每一行存放的都是用戶的query,每個文件的query都可能重復。要求你按照query的頻度排序。
方案1:
1)   順序讀取10個文件,按照hash(query)的結果將query寫入到另外10個文件(記為 )中。這樣新生成的文件每個的大小大約也1G(假設hash函數是隨機的)。
2)   找一台內存在2G左右的機器,依次對 用hash_map(query,  query_count)來統計每個query出現的次數。利用快速/堆/歸並排序按照出現次數進行排序。將排序好的query和對應的query_cout輸出到文件中。這樣得到了10個排好序的文件(記為 )。
3)   對 這10個文件進行歸並排序(內排序與外排序相結合)。
方案2:
一般query的總量是有限的,只是重復的次數比較多而已,可能對於所有的query,一次性就可以加入到內存了。這樣,我們就可以采用trie樹/hash_map等直接來統計每個query出現的次數,然后按出現次數做快速/堆/歸並排序就可以了。
方案3:
與方案1類似,但在做完hash,分成多個文件后,可以交給多個文件來處理,采用分布式的架構來處理(比如MapReduce),最后再進行合並。
 
 
 
3、有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
順序讀文件中,對於每個詞x,取 ,然后按照該值存到5000個小文件(記為 )中。這樣每個文件大概是200k左右。如果其中的有的文件超過了1M大小,還可以按照類似的方法繼續往下分,知道分解得到的小文件的大小都不超過1M。對每個小文件,統計每個文件中出現的詞以及相應的頻率(可以采用trie樹/hash_map等),並取出出現頻率最大的100個詞(可以用含100個結點的最小堆),並把100詞及相應的頻率存入文件,這樣又得到了5000個文件。下一步就是把這5000個文件進行歸並(類似與歸並排序)的過程了。
 
 
4、海量日志數據,提取出某日訪問百度次數最多的那個IP。
首先是這一天,並且是訪問百度的日志中的IP取出來,逐個寫入到一個大文件中。注意到IP是32位的,最多有 個IP。同樣可以采用映射的方法,比如模1000,把整個大文件映射為1000個小文件,再找出每個小文中出現頻率最大的IP(可以采用hash_map進行頻率統計,然后再找出頻率最大的幾個)及相應的頻率。然后再在這1000個最大的IP中,找出那個頻率最大的IP,即為所求。
 
 
5、在2.5億個整數中找出不重復的整數,內存不足以容納這2.5億個整數。
方案1:
采用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)進行,共需內存 內存,還可以接受。然后掃描這2.5億個整數,查看Bitmap中相對應位,如果是00變01,01變10,10保持不變。所描完事后,查看bitmap,把對應位是01的整數輸出即可。
方案2:
也可采用上題類似的方法,進行划分小文件的方法。然后在小文件中找出不重復的整數,並排序。然后再進行歸並,注意去除重復的元素。
 
 
6、海量數據分布在100台電腦中,想個辦法高校統計出這批數據的TOP10。
方案1:
1)   在每台電腦上求出TOP10,可以采用包含10個元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我們首先取前10個元素調整成最小堆,如果發現,然后掃描后面的數據,並與堆頂元素比較,如果比堆頂元素大,那么用該元素替換堆頂,然后再調整為最小堆。最后堆中的元素就是TOP10大。
2)   求出每台電腦上的TOP10后,然后把這100台電腦上的TOP10組合起來,共1000個數據,再利用上面類似的方法求出TOP10就可以了。
 
 
7、怎么在海量數據中找出重復次數最多的一個?
先做hash,然后求模映射為小文件,求出每個小文件中重復次數最多的一個,並記錄重復次數。然后找出上一步求出的數據中重復次數最多的一個就是所求(具體參考前面的題)。
 
 
8、上千萬或上億數據(有重復),統計其中出現次數最多的錢N個數據。
上千萬或上億的數據,現在的機器的內存應該能存下。所以考慮采用hash_map/搜索二叉樹/紅黑樹等來進行統計次數。然后就是取出前N個出現次數最多的數據了,可以用第6題提到的堆機制完成。
 
 
9、1000萬字符串,其中有些是重復的,需要把重復的全部去掉,保留沒有重復的字符串。請怎么設計和實現?
這題用trie樹比較合適,hash_map也應該能行。
 
 
10、一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間復雜度分析。
這題是考慮時間效率。用trie樹統計每個詞出現的次數,時間復雜度是O(n*le)(le表示單詞的平准長度)。然后是找出出現最頻繁的前10個詞,可以用堆來實現,前面的題中已經講到了,時間復雜度是O(n*lg10)。所以總的時間復雜度,是O(n*le)與O(n*lg10)中較大的哪一個。
 
 
11、一個文本文件,找出前10個經常出現的詞,但這次文件比較長,說是上億行或十億行,總之無法一次讀入內存,問最優解。
首先根據用hash並求模,將文件分解為多個小文件,對於單個文件利用上題的方法求出每個文件件中10個最常出現的詞。然后再進行歸並處理,找出最終的10個最常出現的詞。
 
 
12、100w個數中找出最大的100個數。
方案1:在前面的題中,我們已經提到了,用一個含100個元素的最小堆完成。復雜度為O(100w*lg100)。
方案2:采用快速排序的思想,每次分割之后只考慮比軸大的一部分,知道比軸大的一部分在比100多的時候,采用傳統排序算法排序,取前100個。復雜度為O(100w*100)。
方案3:采用局部淘汰法。選取前100個元素,並排序,記為序列L。然后一次掃描剩余的元素x,與排好序的100個元素中最小的元素比,如果比這個最小的要大,那么把這個最小的元素刪除,並把x利用插入排序的思想,插入到序列L中。依次循環,知道掃描了所有的元素。復雜度為O(100w*100)。
 
 
13、尋找熱門查詢:
搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前有一千萬個記錄,這些查詢串的重復讀比較高,雖然總數是1千萬,但是如果去除重復和,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就越熱門。請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。
(1) 請描述你解決這個問題的思路;
(2) 請給出主要的處理流程,算法,以及算法的復雜度。
采用trie樹,關鍵字域存該查詢串出現的次數,沒有出現為0。最后用10個元素的最小推來對出現頻率進行排序。
 
 
14、一共有N個機器,每個機器上有N個數。每個機器最多存O(N)個數並對它們操作。如何找到 個數中的中數?
方案1:先大體估計一下這些數的范圍,比如這里假設這些數都是32位無符號整數(共有 個)。我們把0到 的整數划分為N個范圍段,每個段包含 個整數。比如,第一個段位0到 ,第二段為 到 ,…,第N個段為 到 。然后,掃描每個機器上的N個數,把屬於第一個區段的數放到第一個機器上,屬於第二個區段的數放到第二個機器上,…,屬於第N個區段的數放到第N個機器上。注意這個過程每個機器上存儲的數應該是O(N)的。下面我們依次統計每個機器上數的個數,一次累加,直到找到第k個機器,在該機器上累加的數大於或等於 ,而在第k-1個機器上的累加數小於 ,並把這個數記為x。那么我們要找的中位數在第k個機器中,排在第 位。然后我們對第k個機器的數排序,並找出第 個數,即為所求的中位數。復雜度是 的。
方案2:先對每台機器上的數進行排序。排好序后,我們采用歸並排序的思想,將這N個機器上的數歸並起來得到最終的排序。找到第 個便是所求。復雜度是的。
 
 
15、最大間隙問題。
給定n個實數 ,求着n個實數在實軸上向量2個數之間的最大差值,要求線性的時間算法。
    最先想到的方法就是先對這n個數據進行排序,然后一遍掃描即可確定相鄰的最大間隙。但該方法不能滿足線性時間的要求。故采取如下方法:
1)       找到n個數據中最大和最小數據max和min。
2)       用n-2個點等分區間[min,max],即將[min, max]等分為n-1個區間(前閉后開區間),將這些區間看作桶,編號為 ,且桶 的上界和桶i+1的下屆相同,即每個桶的大小相同。每個桶的大小為: 。實際上,這些桶的邊界構成了一個等差數列(首項為min,公差為 ),且認為將min放入第一個桶,將max放入第n-1個桶。
3)       將n個數放入n-1個桶中:將每個元素 分配到某個桶(編號為index),其中 ,並求出分到每個桶的最大最小數據。
4)       最大間隙:除最大最小數據max和min以外的n-2個數據放入n-1個桶中,由抽屜原理可知至少有一個桶是空的,又因為每個桶的大小相同,所以最大間隙不會在同一桶中出現,一定是某個桶的上界和氣候某個桶的下界之間隙,且該量筒之間的桶(即便好在該連個便好之間的桶)一定是空桶。也就是說,最大間隙在桶i的上界和桶j的下界之間產生 ,一遍掃描即可完成。
 
 
16、將多個集合合並成沒有交集的集合:給定一個字符串的集合,格式如:
   。要求將其中交集不為空的集合合並,要求合並完成的集合之間無交集,例如上例應輸出 。
(1) 請描述你解決這個問題的思路;
(2) 給出主要的處理流程,算法,以及算法的復雜度;
(3) 請描述可能的改進。
   采用並查集。首先所有的字符串都在單獨的並查集中。然后依掃描每個集合,順序合並將兩個相鄰元素合並。例如,對於 ,首先查看aaa和bbb是否在同一個並查集中,如果不在,那么把它們所在的並查集合並,然后再看bbb和ccc是否在同一個並查集中,如果不在,那么也把它們所在的並查集合並。接下來再掃描其他的集合,當所有的集合都掃描完了,並查集代表的集合便是所求。復雜度應該是O(NlgN)的。改進的話,首先可以記錄每個節點的根結點,改進查詢。合並的時候,可以把大的和小的進行合,這樣也減少復雜度。
 
 
17、最大子序列與最大子矩陣問題
數組的最大子序列問題:給定一個數組,其中元素有正,也有負,找出其中一個連續子序列,使和最大。
方案1: 這個問題可以動態規划的思想解決。設 表示以第i個元素 結尾的最大子序列,那么顯然 。基於這一點可以很快用代碼實現。最大子矩陣問題:給定一個矩陣(二維數組),其中數據有大有小,請找一個子矩陣,使得子矩陣的和最大,並輸出這個和。
方案2:可以采用與最大子序列類似的思想來解決。如果我們確定了選擇第i列和第j列之間的元素,那么在這個范圍內,其實就是一個最大子序列問題。如何確定第i列和第j列可以詞用暴搜的方法進行。
 
 
 
 
 
 
 
 
大數據量的問題是很多面試筆試中經常出現的問題,比如baidu、google、騰訊這樣的一些涉及到海量數據的公司經常會問到。下面的方法是我對海量數據的處理方法進行了一個一般性的總結,當然這些方法可能並不能完全覆蓋所有的問題,但是這樣的一些方法也基本可以處理絕大多數遇到的問題。下面的一些問題基本直接來源於公司的面試筆試題目,方法不一定最優,如果你有更好的處理方法,歡迎與我討論。
 
1、Bloom filter
適用范圍:可以用來實現數據字典,進行數據的判重,或者集合求交集
基本原理及要點:
對於原理來說很簡單,位數組+k個獨立hash函數。將hash函數對應的值的位數組置1,查找時如果發現所有hash函數對應位都是1說明存在,很明顯這 個過程並不保證查找的結果是100%正確的。同時也不支持刪除一個已經插入的關鍵字,因為該關鍵字對應的位會牽動到其他的關鍵字。所以一個簡單的改進就是 counting Bloom filter,用一個counter數組代替位數組,就可以支持刪除了。
還有一個比較重要的問題,如 何根據輸入元素個數n,確定位數組m的大小及hash函數個數。當hash函數個數k=(ln2)*(m/n)時錯誤率最小。在錯誤率不大於E的情況 下,m至少要等於n*lg(1/E)才能表示任意n個元素的集合。但m還應該更大些,因為還要保證bit數組里至少一半為0,則m應 該>=nlg(1/E)*lge大概就是nlg(1/E)1.44倍(lg表示以2為底的對數)。
舉個例子我們假設錯誤率為0.01,則此時m應大概是n的13倍。這樣k大概是8個。
注意這里m與n的單位不同,m是bit為單位,而n則是以元素個數為單位(准確的說是不同元素的個數)。通常單個元素的長度都是有很多bit的。所以使用bloom filter內存上通常都是節省的。
擴展: Bloom filter將集合中的元素映射到位數組中,用k(k為哈希函數個數)個映射位是否全1表示元素在不在這個集合中。Countingbloom filter(CBF)將位數組中的每一位擴展為一個counter,從而支持了元素的刪除操作。Spectral Bloom Filter(SBF)將其與集合元素的出現次數關聯。SBF采用counter中的最小值來近似表示元素的出現頻率。
問題實例:給你A、B兩個文件,各存放50億條URL,每條URL占用64字節,內存限制是4G,讓你找出A,B文件共同的URL。如果是三個乃至n個文件呢?
根據這個問題我們來計算下內存的占用,4G=2^32大概是40億*8大概是340億,n=50億,如果按出錯率0.01算需要的大概是650億個bit。現在可用的是340億,相差並不多,這樣可能會使出錯率上升些。另外如果這些urlip是一一對應的,就可以轉換成ip,則大大簡單了。
 
2、Hashing
適用范圍:快速查找,刪除的基本數據結構,通常需要總數據量可以放入內存。
基本原理及要點:
hash函數選擇,針對字符串,整數,排列,具體相應的hash方法。
碰撞處理,一種是open hashing,也稱為拉鏈法;另一種就是closedhashing,也稱開地址法,opened addressing。
擴展:d-lefthashing中的d是多個的意思,我們先簡化這個問題,看一看2-left hashing。2-left hashing指的是將一個哈希表分成長度相等的兩半,分別叫做T1和T2,給T1和T2分別配備一個哈希函數,h1和h2。在存儲一個新的key時,同 時用兩個哈希函數進行計算,得出兩個地址h1[key]和h2[key]。這時需要檢查T1中的h1[key]位置和T2中的h2[key]位置,哪一個位置已經存儲的(有碰撞的)key比較多,然后將新key存儲在負載少的位置。如果兩邊一樣多,比如兩個位置都為空或者都存儲了一個key,就把新key 存儲在左邊的T1子表中,2-left也由此而來。在查找一個key時,必須進行兩次hash,同時查找兩個位置。
問題實例:
1)、海量日志數據,提取出某日訪問百度次數最多的那個IP。
IP的數目還是有限的,最多2^32個,所以可以考慮使用hash將ip直接存入內存,然后進行統計。
 
3、bit-map
適用范圍:可進行數據的快速查找,判重,刪除,一般來說數據范圍是int的10倍以下
基本原理及要點:使用bit數組來表示某些元素是否存在,比如8位電話號碼
擴展:bloom filter可以看做是對bit-map的擴展
問題實例:
1)已知某個文件內包含一些電話號碼,每個號碼為8位數字,統計不同號碼的個數。
8位最多99 999 999,大概需要99m個bit,大概10幾m字節的內存即可。
2)2.5億個整數中找出不重復的整數的個數,內存空間不足以容納這2.5億個整數。
將bit-map擴展一下,用2bit表示一個數即可,0表示未出現,1表示出現一次,2表示出現2次及以上。或者我們不用2bit來進行表示,我們用兩個bit-map即可模擬實現這個2bit-map。
 
4、堆
適用范圍:海量數據前n大,並且n比較小,堆可以放入內存
基本原理及要點:最大堆求前n小,最小堆求前n大。方法,比如求前n小,我們比較當前元素與最大堆里的最大元素,如果它小於最大元素,則應該替換那個最大元 素。這樣最后得到的n個元素就是最小的n個。適合大數據量,求前n小,n的大小比較小的情況,這樣可以掃描一遍即可得到所有的前n元素,效率很高。
擴展:雙堆,一個最大堆與一個最小堆結合,可以用來維護中位數。
問題實例:
1)100w個數中找最大的前100個數。
用一個100個元素大小的最小堆即可。
 
5、雙層桶划分
適用范圍:第k大,中位數,不重復或重復的數字
基本原理及要點:因為元素范圍很大,不能利用直接尋址表,所以通過多次划分,逐步確定范圍,然后最后在一個可以接受的范圍內進行。可以通過多次縮小,雙層只是一個例子。
問題實例:
1) 2.5億個整數中找出不重復的整數的個數,內存空間不足以容納這2.5億個整數。
有點像鴿巢原理,整數個數為2^32,也就是,我們可以將這2^32個數,划分為2^8個區域(比如用單個文件代表一個區域),然后將數據分離到不同的區域,然后不同的區域在利用bitmap就可以直接解決了。也就是說只要有足夠的磁盤空間,就可以很方便的解決。
2) 5億個int找它們的中位數。
這個例子比上面那個更明顯。首先我們將int划分為2^16個區域,然后讀取數據統計落到各個區域里的數的個數,之后我們根據統計結果就可以判斷中位數落到那個區域,同時知道這個區域中的第幾大數剛好是中位數。然后第二次掃描我們只統計落在這個區域中的那些數就可以了。
實 際上,如果不是int是int64,我們可以經過3次這樣的划分即可降低到可以接受的程度。即可以先將int64分成2^24個區域,然后確定區域的第幾大數,在將該區域分成2^20個子區域,然后確定是子區域的第幾大數,然后子區域里的數的個數只有2^20,就可以直接利用direct addr table進行統計了。
 
6、數據庫索引
適用范圍:大數據量的增刪改查
基本原理及要點:利用數據的設計實現方法,對海量數據的增刪改查進行處理。
 
7、倒排索引(Inverted index)
適用范圍:搜索引擎,關鍵字查詢
基本原理及要點:為何叫倒排索引?一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。
以英文為例,下面是要被索引的文本:
T0 = "it is what it is"
T1 = "what is it"
T2 = "it is a banana"
我們就能得到下面的反向文件索引:
"a":      {2}
"banana": {2}
"is":     {0, 1, 2}
"it":     {0, 1, 2}
"what":   {0, 1}
檢索的條件"what","is" 和 "it" 將對應集合的交集。
正向索引開發出來用來存儲每個文檔的單詞的列表。正向索引的查詢往往滿足每個文檔有序頻繁的全文查詢和每個單詞在校驗文檔中的驗證這樣的查詢。在正向索引 中,文檔占據了中心的位置,每個文檔指向了一個它所包含的索引項的序列。也就是說文檔指向了它包含的那些單詞,而反向索引則是單詞指向了包含它的文檔,很 容易看到這個反向的關系。
問題實例:文檔檢索系統,查詢那些文件包含了某單詞,比如常見的學術論文的關鍵字搜索。
 
8、外排序
適用范圍:大數據的排序,去重 。
基本原理及要點:外排序的歸並方法,置換選擇 敗者樹原理,最優歸並樹 。
問題實例:有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16個字節,內存限制大小是1M。返回頻數最高的100個詞。
這個數據具有很明顯的特點,詞的大小為16個字節,但是內存只有1m做hash有些不夠,所以可以用來排序。內存可以當輸入緩沖區使用。
 
9、trie樹
適用范圍:數據量大,重復多,但是數據種類小可以放入內存
基本原理及要點:實現方式,節點孩子的表示方式
擴展:壓縮實現。
問題實例:
1)、有10個文件,每個文件1G, 每個文件的每一行都存放的是用戶的query,每個文件的query都可能重復。要你按照query的頻度排序 。
2)、1000萬字符串,其中有些是相同的(重復),需要把重復的全部去掉,保留沒有重復的字符串。請問怎么設計和實現?
3)、尋找熱門查詢:查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個,每個不超過255字節。
 
10、分布式處理 mapreduce
適用范圍:數據量大,但是數據種類小可以放入內存
基本原理及要點:將數據交給不同的機器去處理,數據划分,結果歸約。
問題實例:
1)、The canonical example applicationof MapReduce is a process to count the appearances of
each different word in a set of documents:
void map(String name, String document):
  // name: document name
  // document: document contents
  for each word w in document:
    EmitIntermediate(w, 1);
 
void reduce(String word, Iterator partialCounts):
  // key: a word
  // values: a list of aggregated partial counts
  int result = 0;
  for each v in partialCounts:
    result += ParseInt(v);
  Emit(result);
Here, each document is split in words, and each word is counted initially witha "1" value by the Map function, using the word as the result key.The framework puts together all the pairs with the same key and feeds them tothe same call to Reduce, thus this function just needs to sum all of its inputvalues to find the total appearances of that word.
2)、海量數據分布在100台電腦中,想個辦法高效統計出這批數據的TOP10。
3)、一共有N個機器,每個機器上有N個數。每個機器最多存O(N)個數並對它們操作。如何找到N^2個數的中數(median)?
經典問題分析:
上千萬or億數據(有重復),統計其中出現次數最多的前N個數據,分兩種情況:可一次讀入內存,不可一次讀入。
可用思路:trie樹+堆,數據庫索引,划分子集分別統計,hash,分布式計算,近似統計,外排序
所謂的是否能一次讀入內存,實際上應該指去除重復后的數據量。如果去重后數據可以放入內存,我們可以為數據建立字典,比如通過 map,hashmap,trie,然后直接進行統計即可。當然在更新每條數據的出現次數的時候,我們可以利用一個堆來維護出現次數最多的前N個數據,當 然這樣導致維護次數增加,不如完全統計后在求前N大效率高。
  如果數據無法放入內存。一方面我們可以考慮上面的字典方法能否被改進以適應這種情形,可以做的改變就是將字典存放到硬盤上,而不是內存,這可以參考數據庫的存儲方法。
  當然還有更好的方法,就是可以采用分布式計算,基本上就是map-reduce過程,首先可以根據數據值或者把數據hash(md5)后的值,將數據按照范 圍划分到不同的機子,最好可以讓數據划分后可以一次讀入內存,這樣不同的機子負責處理各種的數值范圍,實際上就是map。得到結果后,各個機子只需拿出各 自的出現次數最多的前N個數據,然后匯總,選出所有的數據中出現次數最多的前N個數據,這實際上就是reduce過程。
實際上可能想直 接將數據均分到不同的機子上進行處理,這樣是無法得到正確的解的。因為一個數據可能被均分到不同的機子上,而另一個則可能完全聚集到一個機子上,同時還可能存在具有相同數目的數據。比如我們要找出現次數最多的前100個,我們將1000萬的數據分布到10台機器上,找到每台出現次數最多的前 100個,歸並之后這樣不能保證找到真正的第100個,因為比如出現次數最多的第100個可能有1萬個,但是它被分到了10台機子,這樣在每台上只有1千 個,假設這些機子排名在1000個之前的那些都是單獨分布在一台機子上的,比如有1001個,這樣本來具有1萬個的這個就會被淘汰,即使我們讓每台機子選 出出現次數最多的1000個再歸並,仍然會出錯,因為可能存在大量個數為1001個的發生聚集。因此不能將數據隨便均分到不同機子上,而是要根據hash 后的值將它們映射到不同的機子上處理,讓不同的機器處理一個數值范圍。
而外排序的方法會消耗大量的IO,效率不會很高。而上面的分布式方法,也可以用於單機版本,也就是將總的數據根據值的范圍,划分成多個不同的子文件,然后逐個處理。處理完畢之后再對這些單詞的及其出現頻率進行一個歸並。實際上就可以利用一個外排序的歸並過程。
另外還可以考慮近似計算,也就是我們可以通過結合自然語言屬性,只將那些真正實際中出現最多的那些詞作為一個字典,使得這個規模可以放入內存。
 
從海量數據中找出中位數
題目:在一個文件中有 10G 個整數,亂序排列,要求找出中位數。內存限制為 2G。只寫出思路即可(內存限制為2G的意思就是,可以使用2G的空間來運行程序,而不考慮這台機器上的其他軟件的占用內存)。
    關於中位數:數據排序后,位置在最中間的數值。即將數據分成兩部分,一部分大於該數值,一部分小於該數值。中位數的位置:當樣本數為奇數時,中位數=(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是32位無符號整數。
1)       讀一遍10G個整數,把整數映射到256M個區段中,用一個64位無符號整數給每個相應區段記數。說明:整數范圍是0 - 2^32 - 1,一共有4G種取值,映射到256M個區段,則每個區段有16(4 G/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)時空權衡。
花費256個區段也許只是恰好配合2GB的內存(其實也不是,呵呵)。可以增大區段范圍,減少區段數目,節省一些內存,雖然增加第二部分的對單個數值的計數,但第一部分對每個區段的計數加快了(總體改變??待測)。
4)映射時盡量用位操作,由於每個區段的起點都是2的整數冪,映射起來也很方便。
1、有1億個浮點數,請找出其中對大的10000個。提示:假設每個浮點數占4個字節,1億個浮點數就要站到相當大的空間,因此不能一次將全部讀入內存進行排序。
   可以發現如果一次讀入那么機器的內存肯定是受不了的,因此我們只有想其他方法解決,解決方式為了高效還是得符合一定的該概率解決,結果並不一定准確,但是應該可以作對大部分的數據。
我們可以把1億個浮點數分組為100W個一組,這樣就分為了100個組,第一次在每個組中找出最大的1W個數,第二次查詢的時候就是100W個數中再找出最大的1W個數。
PS:100W個數中再找出最大的1W個數用類似快排的思想搞定。
還有一種更效率的思路是:
1)       讀入的頭10000個數,直接創建二叉排序樹。O(1)
2)       對以后每個讀入的數,比較是否比前10000個數中最小的大。(N次比較)如果小的話接着讀下面的數。O(N)
3)       如果大,查找二叉排序樹,找到應當插入的位置。
4)       刪除當前最小的結點。
5)       重復步驟2,直到10億個數全都讀完。
6)       按照中序遍歷輸出當前二叉排序樹中的所有10000個數字。
基本上算法的時間復雜度是O(N)次比較
算法的空間復雜度是10000(常數)
基於上面的想法,可以用最小堆來實現,這樣沒加入一個比10000個樹中最小的數大時的復雜度為log10000.
 
2、有一篇英文文章(也就是說每個單詞之間由空格分隔),請找出“csdn”這個單詞出現的次數,要求效率最高,並寫出算法的時間級。
可以把單詞看成一個N進制數,CSDN相當於('c'-'a')*N^3+('s'-'a')*N^2+('d'-'a')*N+('n'-'a'),然后查找這個數出現的次數就是答案,也可以建立一顆字典樹,然后去計數!
 PS:N可以取32,64等
 
3.假設有1kw個身份證號,以及他們對應的數據。身份證號可能重復,要求找出出現次數最多的身份證號。
    簡單進行hash搞定,O(n),如果數據量再擴大我就不知道怎么搞了,用磁盤的話,IO數據是接受不了的。
 
4、百度每天都會接受數億的查詢請求, 如何在這么多的查詢(Query)中找出高頻的Query是一個不小的挑戰. 而你的任務則更加艱巨, 你需要在極其有限的資源下來找出這些高頻的Query.(使用內存不得多於1MB) 。輸入文件是一行一個Query,以文件結束符結尾。每個Query字節數L(一個漢字兩個字節)滿足:0<=16. 輸入大小不超過1GB(包括換行符)。 輸出你認為最高頻的100個query. 每行一個, 不能有重復, 不能多輸出, 但可以少輸出(見樣例).
     hash,然后建立hash[103][100]的節點的表,每次找出出現次數最少的進行替換。
 
1、像搜索的輸入信息是一個字符串,統計300萬輸入信息中的最熱門的前十條,我們每次輸入的一個字符串為不超過255byte,內存使用只有1G,
請描述思想,寫出算法(C語言),空間和時間復雜度,
2、國內的一些帖吧,如baidu,有幾十萬個主題,假設每一個主題都有上億的跟帖子,怎么樣設計這個系統速度最好,請描述思想,寫出算發(c語言),空間和時間復雜度
第一題:全部存入內存也是可以的300w*255<1G,當然進行字符串hash,然后進行統計
第二題:思想多級索引,第一級對主題進行索引,第二級對帖子,可以用一些復雜的數據結構維護,比方說用B+樹進行維護。
 
題目描述:若有很大一組數據,數據的個數是N(每個數占4個字節),內存大小為M個字節,其中M<4*N,使得不能在現有內存情況下通過直接排序找到這N個數的中位數。
 
騰訊海量數據面試題
1、在一個文件中有10G 個整數,亂序排列,要求找出中位數。內存限制為2G。只寫出思路即可。
    海量數據處理的問題。10G個數,中位數就是第5G、第5G+1個數。回想一下,一般情況下求中位數的做法:類似於快排的partition,找到一個數,使比它小的數的個數占到總數的一半就行。所以,可以把數值空間分段,然后統計每一段中數據的個數,這樣就可以很容易的確定中位數在那一段。找個該段后,數據量已經急劇減小了,剩下的問題就好處理了。這種方法可以說是桶排序的思想,也可以說是hash的思想。下面具體分析一下:
    因為要統計每一段中數據的個數,所以可以用一個unsigned int型。unsignedint一般占4個字節,可以計數到2^32-1,大約是4G。題目中有10G個數,如果有很多數落在同一個段中,unsigned int肯定不夠用。所以,這里的計數用要8字節的long long。即,相當於有一個數組,數組是long long性,數組的每一個元素,代表了一個數據段內的數據個數。這個數組有多大?為了充分利用2G內存,數組大小2G/8= 256M。即,有數組long long cnt[256M].
     假設題目中的10G個數都是4字節的int。如何把這10G個整數,映射到cnt[256M]的數組中。可以使用計算機中的虛擬地址到物理地址的轉換。取int的高28位作為數組下標的索引值,這樣就可以完成映射。
整個算法的流程:
掃描10G個整數,對每個整數,取高28位,映射到數組的某個元素上
給數組的這個元素加1,表示找到一個屬於該數據段的元素
掃描完10G個整數后,數組cnt中就記錄了每段中元素的個數
從第一段開始,將元素個數累計,直到值剛好小於5G,則中位數就在該段
這時對10G個整數再掃描一遍,記錄該段中每個元素的個數。直至累計到5G即可。
2、一個文件中有40億個整數,每個整數為四個字節,內存為1GB,寫出一個算法:求出這個文件里的整數里不包含的一個整數。
方法一:
使用位圖。4字節的int,有4G個不同的值。每個值,對應1bit,則共需 4G/8 =512M 內存。初始狀態,對512M的位圖清零。然后,對這40億個整數進行統計。如果某個值出現了,那么就把這個值對應的bit置位。最后,掃描位圖,找到一個沒有被置位的bit即可。
方法二:
分段統計。Long long cnt[512M/8=64M]對應數值空間的64M個數據段。每個數據段包含64個不同值,用一個longlong作為這個數據段內的位圖,位圖占64M*8=512M。
這樣掃描一遍40億個整數后,從數組中找到一個計數小於64的元素,然后查看它的位圖,找出未出現的元素。
   方法二平均性能應該比方法一快,但它占的內存很恐怖。其實,這兩種方法都不是很實際,總共1G的內存,算法就消耗512M甚至1G,那剩下的系統程序怎么辦?OS都跑不起來了吧。
3、騰訊服務器每秒有2w個QQ號同時上線,找出5min內重新登入的qq號並打印出來。
     這應該是道面試題,面試官隨口問了一下。主要是看思路吧。
     最簡單的想法:直接用STL的set。從某一時刻開始計時,每登陸一個QQ,把它放入set,如果已存則直接打印。直到5min后,就可以over了。下面來簡單分析一下算法的復雜度:
     空間復雜度:用str存儲每個QQ號,假設QQ號有20位,理想情況下每個QQ占20Byte。則5min內的QQ:2w * 60 * 5 = 600w個,需要的存儲空間600w * 20byte = 12000w byte = 120M,這樣的存儲應該可以忍受吧。
      時間復雜度:STL的set是用二叉樹(更確切的說是:紅黑樹)實現的,查找效率是O( lgn ),應該還是挺快的吧。
     呃,有人說不讓用STL。那就自己設計一個數據結構唄。該用什么數據結構呢?想了想,還是繼續用樹,這里用一個trie tree吧。節點內容包括QQ號、指向子節點的指針(這里有10個,認為QQ由0---9的數字組成)。登陸時間要不要?考慮這樣一個問題:是否需要把所有的QQ都保存在內存中?隨着時間的增加,登陸的QQ會越來越多,比較好的方法是把長時間不登陸的QQ釋放掉。所以需要記錄登陸時間,以便於釋放長期不登陸的QQ。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM