1、題目:每一個ip訪問百度,其ip地址都會被記錄到后臺日志文件中,假設一天的訪問日志有100G,求出一天中訪問百度次數最多的ip地址,可以使用的內存大小是1G。
解題
1M `=10^6B 1G`=10^9B
首先解決大文件問題,也就是如何處理100G的一個大文件,這個通常的解決方法就是將大文件分解成許多小文件。我們可以通過對IP地址求hash然后對1024取模將一個100G的大文件分解成1024個小文件(file0,file1......file1023),注意這里的1024個文件并不是平均分的,也就是每個文件大小并不是(100G/1204)。當然我們考慮的時候可以假設文件是平均分的,那么每個文件大小為100M,這樣一個100M的文件是可以全部讀入大小為1G內存中。這樣就解決了第一個文件太大不能一次讀入內存的問題。
考慮到ip地址是32為,那么總共有2^32=4G種可能出現的ip地址,每個ip地址出現的次數不確定,這個具體是由100G大文件決定的。對每個小文件進行處理,我們知道前面每個文件中的ip是通過hash(ip)%1024。這樣相當于將2^32=4G種ip地址進行了分段,每個文件中可能出現的ip最大范圍是4G/1024=4M。創建一個hashmap,讀取小文件中的每個ip地址,判斷hashmap中是否有這個ip,如果沒有,這往haspmap中插入一個的鍵值對,即hashmap.put(ip,1);如果haspmap中已經存在了這個ip,那么求出這個ip所對應的值count=haspmap.get(ip),然后往修改這個ip所對應的value,使其數量增加1,即hashmap.set(ip,count+1)。
當我們求出每個文件中出現次數最大的ip地址以后,我們在比較這1024個文件中的那個ip出現次數最大
關于本題,注意兩點:
Hash取模是一種等價映射,不會存在同一個元素分散到不同小文件中去的情況,即這里采用的是%1000算法,那么同一個IP在hash后,只可能落在同一個文件中,不可能被分散的。
那到底什么是hash映射呢?簡單來說,就是為了便于計算機在有限的內存中處理big數據,從而通過一種映射散列的方式讓數據均勻分布在對應的內存位置(如大數據通過取余的方式映射成小樹存放在內存中,或大文件映射成多個小文件),而這個映射散列方式便是我們通常所說的hash函數,設計的好的hash函數能讓數據均勻分布而減少沖突。盡管數據映射到了另外一些不同的位置,但數據還是原來的數據,只是代替和表示這些原始數據的形式發生了變化而已。
2、搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前一個日志文件中有一千萬個記錄(這些查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門),請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。
解答:
1M `=10^6B 1G`=10^9B
1000萬條記錄,每條記錄最大為255Byte,那么日志文件最大有2.5G左右,大于1G內存。但是題目中又提到這樣的1000萬條記錄中有許多是重復的,出去重復的話只有300萬條記錄,存儲這樣的300萬條記錄需要0.75G左右的內存,小于1G內存。那么我們可以考慮將這些無重復的記錄裝入內存,這是我們需要一種數據結構,這種數據結構即能夠存儲查詢串,又能存儲查詢串的出現次數,我們可以通過hashmap來保存。讀取文件,創建一個hashmap,如果hashmap中存儲了遍歷到的query,則修改該query所對應的count值,使其+1;如果hashmap中沒有這個query,那么往haspmap中插入。這樣我們就創建好了一個包含所有query和次數的hashmap。
然后我們創建一個長度為10最小堆MinHeap(求最多的要用最小堆,求最小的要用最大堆),(這里需要假定這個小頂堆是大頂堆)。最小堆的堆頂元素最小,如果堆頂這個最小的元素都大于其他非堆元素了,那么堆中的其他元素必定大于其他非堆中元素。遍歷hashmap,如果MinHeap未滿,那么往MinHeap中插入這個鍵值對,如果MinHeap滿了,則比較遍歷到的元素的count值堆頂的count,如果遍歷到元素的count大于堆頂count值,刪除堆頂元素,插入當前遍歷到的元素。遍歷完整個hashmap以后,在MinHeap中存儲的就是最熱門10個查詢串。
3、將query按照出現的頻度排序(10個1G大小的文件)。有10個文件,每個文件1G,每個文件的每一行都存放的是用戶的query,每個文件的query都可能重復。如何按照query的頻度排序?求出Top10?
1)讀取10個文件,按照hash(query)%10的結果將query寫到對應的10個文件(file0,file1....file9)中,這樣的10個文件不同于原先的10個文件。這樣我們就有了10個大小約為1G的文件。任意一個query只會出現在某個文件中。
2)對于1)中獲得的10個文件,分別進行如下操作
利用hash_map(query,query_count)來統計每個query出現的次數。
創建一個長度為10的堆來保存一個文件中出現次數最多的hash_map(query,query_count),最后將這10個鍵值對輸出到result文件中。
3)通過2)獲得的result文件保存著每個文件出現次數最多的10條記錄,對其中的100條記錄按照query_count進行排序,最后輸出query_count最大的10條query。
來源:http://www.cnblogs.com/xwdreamer
4、有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
1G`=10^6M
方案:順序讀文件中,對于每個詞x,取hash(x)%5000,然后按照該值存到5000個小文件(記為x0,x1,...x4999)中。這樣每個文件大概是200k左右。
如果其中的有的文件超過了1M大小,還可以按照類似的方法繼續往下分,直到分解得到的小文件的大小都不超過1M。
?對每個小文件,統計每個文件中出現的詞以及相應的頻率(可以采用trie樹/hash_map等),并取出出現頻率最大的100個詞(可以用含100個結點的最小堆),并把100個詞及相應的頻率存入文件,這樣又得到了5000個文件。
下一步就是把這5000個文件進行歸并(類似與歸并排序)的過程了。這個時候就和上面的第三題很類似了,相同的Hashcode%1000放在一個文件中.
5、 給定a、b兩個文件,各存放50億個url,每個url各占64字節,內存限制是4G,讓你找出a、b文件共同的url?
方案1:可以估計每個文件安的大小為5G×64=320G,遠遠大于內存限制的4G。所以不可能將其完全加載到內存中處理??紤]采取分而治之的方法。
1. 遍歷文件a,對每個url求取hash(url)%1000,然后根據所取得的值將url分別存儲到1000個小文件(記為a0,a1,...,a999)中。這樣每個小文件的大約為300M。
2. 遍歷文件b,采取和a相同的方式將url分別存儲到1000小文件(記為b0,b1,...,b999)。這樣處理后,所有可能相同的url都在對應的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不對應的小文件不可能有相同的url。然后我們只要求出1000對小文件中相同的url即可。
3. 求每對小文件中相同的url時,可以把其中一個小文件的url存儲到hash_set中。然后遍歷另一個小文件的每個url,看其是否在剛才構建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
方案2:如果允許有一定的錯誤率,可以使用Bloom filter,4G內存大概可以表示340億bit。將其中一個文件中的url使用Bloom filter映射為這340億bit,然后挨個讀取另外一個文件的url,檢查是否與Bloom filter,如果是,那么該url應該是共同的url(注意會有一定的錯誤率)。
Bloom filter日后會在本BLOG內詳細闡述。
6、在2.5億個整數中找出不重復的整數,注,內存不足以容納這2.5億個整數。
方案1:采用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)進行,共需內存內存,還可以接受。然后掃描這2.5億個整數,查看Bitmap中相對應位,如果是00變01,01變10,10保持不變。所描完事后,查看bitmap,把對應位是01的整數輸出即可
方案2:
遍歷文件求hash(num)%10000,分成1萬個小文件,對每個小文件找出不充分的數
對于每個小文件,利用Hash key = num,value =次數,最后發現此時是1的就是只出現一次的整數
匯總1萬個小文件的結果
如果分成1萬個小文件,部分文件過大,也可以繼續進行分成小文件
7、給40億個不重復的unsigned int的整數,沒排過序的,然后再給一個數,如何快速判斷這個數是否在那40億個數當中?
與上第6題類似,我的第一反應時快速排序+二分查找。以下是其它更好的方法:方案1:oo,申請512M的內存,一個bit位代表一個unsigned int值。讀入40億個數,設置相應的bit位,讀入要查詢的數,查看相應bit位是否為1,為1表示存在,為0表示不存在。
dizengrong:方案2:這個問題在《編程珠璣》里有很好的描述,大家可以參考下面的思路,探討一下:又因為2^32為40億多,所以給定一個數可能在,也可能不在其中;這里我們把40億個數中的每一個用32位的二進制來表示假設這40億個數開始放在一個文件中。
然后將這40億個數分成兩類: 1.最高位為0 2.最高位為1 并將這兩類分別寫入到兩個文件中,其中一個文件中數的個數<=20億,而另一個>=20億(這相當于折半了);與要查找的數的最高位比較并接著進入相應的文件再查找
再然后把這個文件為又分成兩類: 1.次最高位為0 2.次最高位為1
并將這兩類分別寫入到兩個文件中,其中一個文件中數的個數<=10億,而另一個>=10億(這相當于折半了); 與要查找的數的次最高位比較并接著進入相應的文件再查找。 ....... 以此類推,就可以找到了,而且時間復雜度為O(logn),方案2完。
上面講的無法理解
我感覺這樣應該也可以:
計算Hash(num)%10000,分成1萬個小文件,求hash(target)%10000,找到所在的小文件區域,若與1萬個文件的hash(num)%10000值不一樣,說明不存在。
對于存在的時候,找到這個小文件,繼續進行上面的操作,(%1000,或者更小,這里根據內存大小而定)
若果存在一個小文件的hash(num)%1000 == hash(target)%1000 ,只需要順序的遍歷判斷是否存在這個數了。
附:這里,再簡單介紹下,位圖方法: 使用位圖法判斷整形數組是否存在重復 判斷集合中存在重復是常見編程任務之一,當集合中數據量比較大時我們通常希望少進行幾次掃描,這時雙重循環法就不可取了。
位圖法比較適合于這種情況,它的做法是按照集合中最大元素max創建一個長度為max+1的新數組,然后再次掃描原數組,遇到幾就給新數組的第幾位置上1,如遇到5就給新數組的第六個元素置1,這樣下次再遇到5想置位時發現新數組的第六個元素已經是1了,這說明這次的數據肯定和以前的數據存在著重復。這種給新數組初始化時置零其后置一的做法類似于位圖的處理方法故稱位圖法。它的運算次數最壞的情況為2N。如果已知數組的最大值即能事先給新數組定長的話效率還能提高一倍。
8、怎么在海量數據中找出重復次數最多的一個?
方案1:先做hash,然后求模映射為小文件,求出每個小文件中重復次數最多的一個,并記錄重復次數。然后找出上一步求出的數據中重復次數最多的一個就是所求(具體參考前面的題)。
9、上千萬或上億數據(有重復),統計其中出現次數最多的N個數據。
方案1:上千萬或上億的數據,現在的機器的內存應該能存下。所以考慮采用hash_map/搜索二叉樹/紅黑樹等來進行統計次數。然后就是取出前N個出現次數最多的數據了,可以用第2題提到的堆機制完成。
10、一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間復雜度分析。
方案1:這題是考慮時間效率。用trie樹統計每個詞出現的次數,時間復雜度是O(n*le)(le表示單詞的平準長度)。然后是找出出現最頻繁的前10個詞,可以用堆來實現,前面的題中已經講到了,時間復雜度是O(n*lg10)。所以總的時間復雜度,是O(n*le)與O(n*lg10)中較大的哪一個。
附、100w個數中找出最大的100個數。
方案1:在前面的題中,我們已經提到了,用一個含100個元素的最小堆完成。復雜度為O(100w*lg100)。
方案2:采用快速排序的思想,每次分割之后只考慮比軸大的一部分,知道比軸大的一部分在比100多的時候,采用傳統排序算法排序,取前100個。復雜度為O(100w*100)。
方案3:采用局部淘汰法。選取前100個元素,并排序,記為序列L。然后一次掃描剩余的元素x,與排好序的100個元素中最小的元素比,如果比這個最小的要大,那么把這個最小的元素刪除,并把x利用插入排序的思想,插入到序列L中。依次循環,知道掃描了所有的元素。復雜度為O(100w*100)。
來源:http://www.cnblogs.com/zuoyuan/p/4747635.html