面試筆記–海量數據題目處理總結


面試筆記–海量數據題目處理總結

何謂海量數據處理?

所謂海量數據處理,無非就是基於海量數據上的存儲、處理、操作。何謂海量,就是數據量太大,所以導致要么是無法在較短時間內迅速解決,要么是數據太大,導致無法一次性裝入內存。

那解決辦法呢?針對時間,我們可以采用巧妙的算法搭配合適的數據結構,如Bloom filter/Hash/bit-map/堆/數據庫或倒排索引/trie樹,針對空間,無非就一個辦法:大而化小,分而治之(hash映射),你不是說規模太大嘛,那簡單啊,就把規模大化為規模小的,各個擊破不就完了嘛。

至於所謂的單機及集群問題,通俗點來講,單機就是處理裝載數據的機器有限(只要考慮cpu,內存,硬盤的數據交互),而集群,機器有多輛,適合分布式處理,並行計算(更多考慮節點和節點間的數據交互)。

海量數據處理的方法

處理海量數據問題,無非就是 6 種方法:

分而治之/hash映射 + hash統計 + 堆/快速/歸並排序;

  1. 分而治之/hash映射:針對數據太大,內存受限,只能把大文件化成(取模映射)小文件
  2. hash_map統計:當大文件轉化了小文件,那么我們便可以采用常規的hash_map(key,value)來進行頻率統計。
  3. 堆/快速排序:統計完了之后,便進行排序(可采取堆排序),得到次數最多的key。

多層划分

多層划分—-其實本質上還是分而治之的思想,重在“分”的技巧上!
適用范圍:第k大,中位數,不重復或重復的數字
基本原理及要點:因為元素范圍很大,不能利用直接尋址表,所以通過多次划分,逐步確定范圍,然后最后在一個可以接受的范圍內進行。

Bloom filter/Bitmap

Bloom filter

適用范圍:可以用來實現數據字典,進行數據的判重,或者集合求交集
基本原理:當一個元素被加入集合時,通過K個Hash函數將這個元素映射成一個位陣列(Bit array)中的K個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢索元素一定不在;如果都是1,則被檢索元素很可能在。

Bloom Filter的這種高效是有一定代價的:在判斷一個元素是否屬於某個集合時,有可能會把不屬於這個集合的元素誤認為屬於這個集合(false positive)。因此,Bloom Filter不適合那些“零錯誤”的應用場合。而在能容忍低錯誤率的應用場合下,Bloom Filter通過極少的錯誤換取了存儲空間的極大節省。

Bitmap

Bit-map就是用一個bit位來標記某個元素對應的Value, 而Key即是該元素。由於采用了Bit為單位來表示某個元素是否存在,因此在存儲空間方面,可以大大節省。

Bitmap排序方法
第一步,將所有的位都置為0,從而將集合初始化為空。
第二步,通過讀入文件中的每個整數來建立集合,將每個對應的位都置為1。
第三步,檢驗每一位,如果該位為1,就輸出對應的整數。

Bloom filter可以看做是對bit-map的擴展

Trie樹/數據庫/倒排索引

Trie樹

適用范圍:數據量大,重復多,但是數據種類小可以放入內存
基本原理及要點:實現方式,節點孩子的表示方式
擴展:壓縮實現。

數據庫索引

適用范圍:大數據量的增刪改查
基本原理及要點:利用數據的設計實現方法,對海量數據的增刪改查進行處理。

倒排索引(Inverted index)

適用范圍:搜索引擎,關鍵字查詢
基本原理及要點:一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。

外排序

適用范圍:大數據的排序,去重
基本原理及要點:外排序的歸並方法,置換選擇敗者樹原理,最優歸並樹

分布式處理之Hadoop/Mapreduce

MapReduce是一種計算模型,簡單的說就是將大批量的工作(數據)分解(MAP)執行,然后再將結果合並成最終結果(REDUCE)。這樣做的好處是可以在任務被分解后,可以通過大量機器進行並行計算,減少整個操作的時間。
適用范圍:數據量大,但是數據種類小可以放入內存
基本原理及要點:將數據交給不同的機器去處理,數據划分,結果歸約。

海量數據處理題目

1.海量日志數據,提取出某日訪問百度次數最多的那個IP

  1. 分而治之/hash映射:針對數據太大,內存受限,只能是把大文件化成(取模映射)小文件;按照IP地址的Hash(IP)%1000值,把海量IP日志分別存儲到1000個小文件中。這樣,每個小文件最多包含4MB個IP地址
  2. hash_map統計:當大文件轉化了小文件,那么我們便可以采用常規的hash_map(ip,value)來進行頻率統計。
  3. 堆/快速排序:統計完了之后,可以得到1024個小文件中的出現次數最多的IP,再依據常規的排序算法得到總體上出現次數最多的IP;

Hash取模是一種等價映射,不會存在同一個元素分散到不同小文件中的情況,即這里采用的是mod1000算法,那么相同的IP在hash取模后,只可能落在同一個文件中,不可能被分散的。因為如果兩個IP相等,那么經過Hash(IP)之后的哈希值是相同的,將此哈希值取模(如模1000),必定仍然相等。

2.尋找熱門查詢,300萬個查詢字符串中統計最熱門的10個查詢

原題:搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前有一千萬個記錄(這些查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門),請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。

數據規模大,一次處理不了,我們就需要將數據通過hash映射切分;而本題的情況屬於數據量可以一次放入內存(300萬個字符串假設沒有重復,都是最大長度,那么最多占用內存3M*1K/4=0.75G。所以可以將所有字符串都存放在內存中進行處理),所以只是需要一個合適的數據結構

所以我們在此直接讀數據進行hash統計,統計后的數據只有0.75G,可以直接進行排序,而對這種TopK問題,一般是采用堆來解決。

  1. hash_map統計:先對這批海量數據預處理。具體方法是:維護一個Key為Query字串,Value為該Query出現次數的HashMap,即hashmap(Query,Value),每次讀取一個Query,如果該字串不在HashMap中,那么加入該字串,並且將Value值設為1;如果該字串在HashMap中,那么將該字串的計數加一即可。最終我們在O(N)的時間復雜度內用Hash表完成了統計;
  2. 堆排序:借助堆這個數據結構,找出Top K,時間復雜度為O(NlogK)。因此,維護一個K(該題目中是10)大小的小根堆,然后遍歷300萬的Query,分別和根元素進行對比。所以,我們最終的時間復雜度是:O(N) + N’ * O(logK),(N為1000萬,N’為300萬)。

方案2:
采用trie樹,關鍵字域存該查詢串出現的次數,沒有出現為0。最后用10個元素的最小推來對出現頻率進行排序。

3.有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。

  1. 分而治之/hash映射:順序讀文件中,對於每個詞x,取hash(x)%5000,然后按照該值存到5000個小文件(記為x0,x1,…x4999)中。這樣每個文件大概是200k左右。如果其中的有的文件超過了1M大小,還可以按照類似的方法繼續往下分,直到分解得到的小文件的大小都不超過1M。
  2. hash_map統計:對每個小文件,采用trie樹/hash_map等統計每個文件中出現的詞以及相應的頻率。
  3. 堆/歸並排序:取出出現頻率最大的100個詞(可以用含100個結點的最小堆)后,再把100個詞及相應的頻率存入文件,這樣又得到了5000個文件。最后就是把這5000個文件進行歸並(類似於歸並排序)的過程了。

4.海量數據分布在100台電腦中,想個辦法高效統計出這批數據的TOP10。

如果每個數據元素只出現一次,而且只出現在某一台機器中,那么可以采取以下步驟統計出現次數TOP10的數據元素:

  1. 如果每個數據元素只出現一次,而且只出現在某一台機器中,那么可以采取以下步驟統計出現次數TOP10的數據元素:
  2. 求出每台電腦上的TOP10后,然后把這100台電腦上的TOP10組合起來,共1000個數據,再利用上面類似的方法求出TOP10就可以了。

但如果同一個元素重復出現在不同的電腦中,則有兩種方法:

  • 遍歷一遍所有數據,重新hash取摸,如此使得同一個元素只出現在單獨的一台電腦中,然后采用上面所說的方法,統計每台電腦中各個元素的出現次數找出TOP10,繼而組合100台電腦上的TOP10,找出最終的TOP10。
  • 暴力求解:直接統計統計每台電腦中各個元素的出現次數,然后把同一個元素在不同機器中的出現次數相加,最終從所有數據中找出TOP10。

5.有10個文件,每個文件1G,每個文件的每一行存放的都是用戶的query,每個文件的query都可能重復。要求你按照query的頻度排序。

方案1:

  1. hash映射:順序讀取10個文件,按照hash(query)%10的結果將query寫入到另外10個文件(記為a0,a1,..a9)中。這樣新生成的文件每個的大小大約也1G(假設hash函數是隨機的)。
  2. hash_map統計:找一台內存在2G左右的機器,依次對用hash_map(query, query_count)來統計每個query出現的次數。注:hash_map(query,query_count)是用來統計每個query的出現次數,不是存儲他們的值,出現一次,則count+1。
  3. 堆/快速/歸並排序:利用快速/堆/歸並排序按照出現次數進行排序,將排序好的query和對應的query_cout輸出到文件中,這樣得到了10個排好序的文件(記為)。最后,對這10個文件進行歸並排序(內排序與外排序相結合)

方案2:
一般query的總量是有限的,只是重復的次數比較多而已,可能對於所有的query,一次性就可以加入到內存了。這樣,我們就可以采用trie樹/hash_map等直接來統計每個query出現的次數,然后按出現次數做快速/堆/歸並排序就可以了。

方案3:
與方案1類似,但在做完hash,分成多個文件后,可以交給多個文件來處理,采用分布式的架構來處理(比如MapReduce),最后再進行合並。

6.給定a、b兩個文件,各存放50億個url,每個url各占64字節,內存限制是4G,讓你找出a、b文件共同的url?

可以估計每個文件安的大小為5G×64=320G,遠遠大於內存限制的4G。所以不可能將其完全加載到內存中處理。考慮采取分而治之的方法。

方案1:

  1. 分而治之/hash映射:遍歷文件a,對每個url求取,然后根據所取得的值將url分別存儲到1000個小文件(記為a0,a1,…,a999)中。這樣每個小文件的大約為300M。遍歷文件b,采取和a相同的方式將url分別存儲到1000小文件中(記為b0,b1,…,b999)。這樣處理后,所有可能相同的url都在對應的小文件(a0vsb0,a1vsb1,…,a999vsb999)中,不對應的小文件不可能有相同的url。然后我們只要求出1000對小文件中相同的url即可。
  2. hash_set統計:求每對小文件中相同的url時,可以把其中一個小文件的url存儲到hash_set中。然后遍歷另一個小文件的每個url,看其是否在剛才構建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

方案2:
如果允許有一定的錯誤率,可以使用Bloom filter,4G內存大概可以表示340億bit。將其中一個文件中的url使用Bloom filter映射為這340億bit,然后挨個讀取另外一個文件的url,檢查是否與Bloom filter,如果是,那么該url應該是共同的url(注意會有一定的錯誤率)。

7.在2.5億個整數中找出不重復的整數,注,內存不足以容納這2.5億個整數。

方案1:

  1. 分而治之/hash映射
  2. hashmap統計
  3. 找出所有value為1的key值

方案2:
采用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)進行,共需內存2^32 * 2 bit=1 GB內存,還可以接受。然后掃描這2.5億個整數,查看Bitmap中相對應位,如果是00變01,01變10,10保持不變。所描完事后,查看bitmap,把對應位是01的整數輸出即可。

8.怎么在海量數據中找出重復次數最多的一個?

  1. 先做hash,然后求模映射為小文件,
  2. 通過hashmap求出每個小文件中重復次數最多的一個,並記錄重復次數。
  3. 然后比較所有小文件中出現最多的數,最大的就是重復次數最多

9.上千萬或上億數據(有重復),統計其中出現次數最多的前N個數據。

上千萬或上億的數據,現在的機器的內存應該能存下。所以考慮采用hash_map來進行統計次數。然后利用堆取出前N個出現次數最多的數據。

10.一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間復雜度分析。

方案1:
如果文件比較大,無法一次性讀入內存,可以采用hash取模的方法,將大文件分解為多個小文件,對於單個小文件利用hash_map統計出每個小文件中10個最常出現的詞,然后再進行歸並處理,找出最終的10個最常出現的詞。

方案2:
通過hash取模將大文件分解為多個小文件后,除了可以用hash_map統計出每個小文件中10個最常出現的詞,也可以用trie樹統計每個詞出現的次數,時間復雜度是O(n*le)(le表示單詞的平准長度),最終同樣找出出現最頻繁的前10個詞(可用堆來實現),時間復雜度是O(nlog10)

11.1000萬字符串,其中有些是重復的,需要把重復的全部去掉,保留沒有重復的字符串。請怎么設計和實現?

方案1:
這題用trie樹比較合適,hash_map也行。

方案2:
分而治之/hash映射 + hashmap

12.一個文本文件,找出前10個經常出現的詞,但這次文件比較長,說是上億行或十億行,總之無法一次讀入內存,問最優解。

  1. 首先根據用hash並求模,將文件分解為多個小文件,
  2. 對於單個文件利用hashmap求出每個文件件中10個最常出現的詞。
  3. 然后再進行歸並處理,找出最終的10個最常出現的詞。

13.騰訊面試題:給40億個不重復的unsigned int的整數,沒排過序的,然后再給一個數,如何快速判斷這個數是否在那40億個數當中?

方案1:
申請512M的內存,一個bit位代表一個unsigned int值。讀入40億個數,設置相應的bit位,讀入要查詢的數,查看相應bit位是否為1,為1表示存在,為0表示不存在。

14.10億個QQ號,讓我找出一個QQ號是不是在其中,時間復雜度要求O(1)

用bitmap來做這個問題。首先對數據進行預處理。定義10億bit位個int.在32位計算機下,一個int是32位,10億位的話,就需要10億除以32個int整數。大概有很多個。第一個int標記0-31這個數字范圍的QQ號存不存在,比如說0000001這個QQ號,我就把第一個int的第1位置1。第二個int能夠標記32-63這個范圍的QQ存不存在,以此類推。把這10億個QQ號預處理一遍。然后計算你給我的這個QQ號,它是在哪個int里面,然后找到相應的數據位,看是1還是0,就能在O(1)的時間里找到

15.移動公司需要對已經發放的所有139段的號碼進行統計排序,已經發放的139號碼段的文件都存放在一個文本文件中(原題是放在兩個文件中),一個號碼一行,現在需要將文件里的所有號碼進行排序,並寫入到一個新的文件中;號碼可能會有很多,最多可能有一億個不同的號碼(所有的139段號碼),存入文本文件中大概要占1.2G的空間;jvm最大的內存在300以內,程序要考慮程序的可執行性及效率;只能使用Java標准庫,不得使用第三方工具。

方案1:

  1. 順序讀取存放號碼文件的中所有號碼,並取139之后的八位轉換為int類型;每讀取號碼數滿一百萬個(這個數據可配置)將已經讀取的號碼排序並存入新建的臨時文件。
  2. 將所有生成的號碼有序的臨時文件合並存入結果文件。

這個算法雖然解決了空間問題,但是運行效率極低,由於IO讀寫操作太多,加上步驟1中的排序的算法(快速排序)本來效率就不高

方案2:bitmap
一個號碼占一個bit,一共需要 99999999 bit ,一個int32位,所以需要312.5萬個int值,即1250萬Byte = 12.5M,算法如下

    1. 初始化bits[capacity];
    2. 順序所有讀入電話號碼,並轉換為int類型,修改位向量值bits[phoneNum]=1;
    3. 遍歷bits數組,如果bits[index]=1,轉換index為電話號碼輸出。


免責聲明!

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



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