數據挖掘、數據分析、海量數據處理的面試題(總結july的博客)

原文地址:http://blog.csdn.net/zy825316/article/details/35600653

緣由

由於有面試通知,現在複習一下十道和海量數據處理相關的題。兩篇博客已經講的非常完備了,但是我怕讀懂了並非真的懂,所以必須自己複述一遍。
  1. 教你如何迅速秒殺掉:99%的海量數據處理面試題
  2. 海量數據處理:十道面試題與十個海量數據處理方法總結
  3. MapReduce技術的初步瞭解與學習

面試歸類

下面6個方面覆蓋了大多數關於海量數據處理的面試題:
  1. 分而治之/hash映射 + hash統計 + 堆/快速/歸併排序;
  2. 雙層桶劃分
  3. Bloom filter/Bitmap;
  4. Trie樹/數據庫/倒排索引;
  5. 外排序;
  6. 分佈式處理之Hadoop/Mapreduce。
下面我講針對上兩篇博客裏的海量數據處理的題的解法再複述一遍。

第一類:分治後hash統計再排序

第一題:海量日誌數據,提取出某日訪問百度次數最多的那個IP

解答:
該題解題思路總共分爲三步
  1. 分而治之/hash映射:如果該文件過大,不能全部讀入內存。我們就必須先利用hash函數將其分割成若干小文件。再分別對各個小文件進行處理。注意這一步我們肯定會將相同的ip放在同一個文件。由於這個題幹給的比較少。我只有自己腦補一下,大概給我們的日誌中就是百度web服務器自己記錄的來自不同的ip的訪問。這個日誌是按天生成的,所以現在我們要統計一天內,哪一個ip訪問本網站百度的次數最多。那麼日誌中肯定就是記錄是訪問ip和時間,但是相同的ip肯定是單獨的一條。所以現在要將相同的ip放在同一個文件中處理。由於我們確定某個ip放到哪一個子文件的時候是使用的一個hash函數,對一個ip用hash函數求值後取模,就可以確定這個條ip記錄放到哪一文件。所有在相同的ip一定存在一個文件,這樣方便我們統計。
  2. hash_map統計:針對每一個文件,我們肯定是可以讀入內存了。(因爲這裏每一個文件的大小是我們在第一步控制的)。針對內存中的每一條記錄,以ip爲鍵,以其出現的次數爲值,遍歷該文件中的每一條記錄。(順便深入思考一下:這裏所謂的hash_map就是一個底層以hashtable實現的數據結構。我猜想底層也就是有一個巨大的數組,數組下標就是鍵的hash值,而數組的內容就是值。在這裏就是用ip算出了數組下標,然後數組內容就是該ip出現的次數。當然更多內容需要去探索,比如這個數組大小是如何確定?發生衝突是如何解決。不過這點與該題無關了,我們只是利用hashmap這個數據結構。)
  3. 堆/快速排序:針對每一個文件,只需遍歷一次就可以取得該文件,ip次數出現最多的那個ip。將所有小文件中訪問次數最多的ip一對比,就可以得到整個文件中ip最多的那一個。

第二題:尋找熱門查詢,300萬個查詢字符串中統計最熱門的10個查詢

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

解答:
有了一題的思路,那麼這道第二題就更好理解了,思路肯定還是分而治之/hash映射 + hash統計 + 堆/快速/歸併排序。但是我們需要考慮第一步是否有必要,因爲共有記錄300萬條,就算每條記錄的長度都是255字節,那麼就是
(3000000*1/4)1024 約等於 (3000000*1/4)1000 ,結果單位爲MB(注意1/4的單位是kb),那麼就是0。75g。所以這整份文件是可以輕易讀入大小爲1G的內存的。所以第一步分而治之/hash映射在本題中是不需要的。
直接第二、三步:
hash統計:遍歷這1000萬條記錄,每天都以一個query爲鍵,其出現的累計出現的次數爲值。那麼就可以在一次遍歷的過程中將所有的統計query出現的次數統計完。
堆/快速/歸併排序:由於我們只需要求出前10個query。所以也就是求top k的問題,那麼使用堆結構。我們可以logK的時間內完成查找和調整堆結構。所以,我們只需要維護一個大小爲10的最小堆。我們確實是求查詢次數最多的10個次,所以我們該用最小堆。接着,就將先將300萬條記錄的前10個插入最小堆,調整最小堆,然後將第11個與最小堆的堆頂比較,如何堆頂元素大,那麼不做任何改變,如果堆頂元素小,那麼就用第11個替換掉堆頂元素,然後調整最小堆,與第12個進行比較。那麼我們在一次遍歷之後就可以選出最大的10個。

第三題

3、有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
解答:
顯然過程還是分而治之/hash映射 + hash統計 + 堆/快速/歸併排序。而且題中已經說的很明顯了,就是內存肯定是不夠用的。所以我們肯定要進行分而治之/hash映射這一步。我就不再複述了,因爲和上面兩道題和相似。

第四題

4、海量數據分佈在100臺電腦中,想個辦法高效統計出這批數據的TOP10。
解答
還是分而治之/hash映射 + hash統計 + 堆/快速/歸併排序 這個思想。
但是我覺得如果面試官如果問類似的題的話,一定要注意反問面試官,相同的數據是不是存在在同一臺電腦上,還有沒有沒內存大小的限制(這個問題只要題中沒有說明有什麼限制,我都覺得需要問一下,體現自己思維比較全面)。針對這道題,如果相同的數據不在同一臺電腦上,那麼就先要進行分而治之/hash映射,將相同的文件放於同一臺電腦上,有點像將相同的數據處於同一個文件中。其他進行的過程與本題其他內容類似,不再贅述。

第五題

5、有10個文件,每個文件1G,每個文件的每一行存放的都是用戶的query,每個文件的query都可能重複。要求你按照query的頻度排序。
解答:
本題不再是選出top N的問題。而是排序的問題,但是核心還是不變。
分而治之/hash映射:由於沒有說相同的query是放在同一個文件,所以必須先對每一個query做一個映射,將相同的query放在同一個文件。所以注意這裏會產生10個新的文件(相同query在同一個文件)。
hash統計:主要選一個2g的電腦來對每一個文件完成這個過程。因爲每個文件的容量都爲1g。

堆/快速/歸併排序:先利用堆/快速/歸併排序對每一份文件的query和query_cout的進行排序,然後將排好序的內容再輸出到一份新文件中。最後對着10文件進行歸併排序(內排序和外排序一起進行)原博客有一份實現:

  • https://github.com/ooooola/sortquery/blob/master/querysort.py

  • 本題一個要點就是:在最後一步的在做完hash,分成多個文件後,可以採用分佈式的架構來處理(比如MapReduce)。我記得hadoop的第一個統計單詞的個數的小例子就是讀取不同文件的單詞的次數,然後輸出的結果就是一個文本,裏面有每一個在單詞在所有文件中出現的總次數。

第六題

6、 給定a、b兩個文件,各存放50億個url,每個url各佔64字節,內存限制是4G,讓你找出a、b文件共同的url?
解答:
5000 000 000 * 64 約等於 5g*64 = 320G。顯然一個內存裝不去。所以需要分而治之/hash映射。
分而治之/hash映射:我們可以考慮將這個50億個url分到1000個小文件中,這樣每個文件也就是300m。主要在映射中,我們對a文件和b文件使用的是同一個hash函數,這樣就能保證如何a文件和b文件中有相同的url,那麼將會被映射到對應的文件中去。比如a0對應b0文件,a1對應b1文件...
hash_set統計:第二步非常簡單了,我們只需要對每一對小文件進行處理。先將a0文件的內容儲存到hash_set中,再依次計算b0的,如果有相同的那就是a與b共有的文件,所以將該條url存儲到最終結果的文件中既可。


第七題

7、怎麼在海量數據中找出重複次數最多的一個?
解答:
簡單說,就是hash映射成小文件,然後hash求出每個數據出現的次數,將每個文件中次數最多的一比較就可以了。

第八題

8、上千萬或上億數據(有重複),統計其中出現次數最多的前N個數據。
解答:
與第二題類似。

第九題

9、一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間複雜度分析。
解答:
除了使用前面的分而治之/hash映射 + hash統計 + 堆/快速/歸併排序的方法以外,也可以使用單詞查找樹。下面是單詞查找樹的分析:
這題是考慮時間效率。用trie樹(單詞查找樹,字典樹,就是一種樹有插入、查找,刪除等操作)統計每個詞出現的次數,時間複雜度是O(n*le)(le表示單詞的字符串長度)。因爲在面對每一個單詞的時候,我們都可以在字典書中進行查找,查看是否有這個單詞,然後將相應的次數加一,之後就是找出出現最頻繁的前10個詞,可以用堆來實現,前面的題中已經講到了,時間複雜度是O(n*lg10)。所以總的時間複雜度,是O(n*le)與O(n*lg10)中較大的哪一個。

第十題

10. 1000萬字符串,其中有些是重複的,需要把重複的全部去掉,保留沒有重複的字符串。請怎麼設計和實現?
想了想和前面的題都類似的,即可用字典樹,也可以用hashmap,原博客對該題還做了性能上的比較。有興趣者自行參看。

第十一題

11. 一個文本文件,找出前10個經常出現的詞,但這次文件比較長,說是上億行或十億行,總之無法一次讀入內存,問最優解。
首先根據用hash並求模,將文件分解爲多個小文件,對於單個文件利用上題的方法求出每個文件件中10個最常出現的詞。然後再進行排序處理,找出最終的10個最常出現的詞。

第十二題

12. 100w個數中找出最大的100個數。
最小堆直接秒殺,複雜度爲O(100w*lg100)。

第二、三類:雙層桶劃分、Bloom filter/Bitmap。

雙層桶劃分、Bloom filter/Bitmap我發現兩者實際上還是解決的同一個問題。雙層桶劃分有點難理解,我今天就算了。

Bloom filter我已經寫過博客總結了:

bitmap

下面就是bitmap,參見百度百科的例子,我覺得這個東西很像計數排序。

假設我們要對0-7內的5個元素(4,7,2,5,3)排序(這裏假設這些元素沒有重複)。那麼我們就可以採用Bit-map的方法來達到排序的目的。要表示8個數,我們就只需要8個Bit(1Bytes)(爲什麼是8個,因爲最大的數是7,試想,如果最大的數是10,我們必須要11個bit,這是因爲我們需要將需要排序的數作爲下標),首先我們開闢1Byte的空間,將這些空間的所有Bit位都置爲0
然後遍歷這5個元素,首先第一個元素是4,那麼就把4對應的位置爲1(可以這樣操作 p+(i/8)|(0×01<<(i%8)) 當然了這裏的操作涉及到Big-ending和Little-ending的情況,這裏默認爲Big-ending),因爲是從零開始的,所以要把第五位置爲1。
然後再處理第二個元素7,將第八位置爲1,,接着再處理第三個元素,一直到最後處理完所有的元素,將相應的位置爲1。
然後我們現在遍歷一遍Bit區域,將該位是一的位的編號輸出(2,3,4,5,7),這樣就達到了排序的目的。其實就是把計數排序用的統計數組的每個單位縮小成bit級別的布爾數組。
然後我們再看看利用這個三個技術能解決的問題,主要是bloom filter/Bitmap

第一題

給你A,B兩個文件,各存放50億條URL,每條URL佔用64字節,內存限制是4G,讓你找出A,B文件共同的URL。如果是三個乃至n個文件呢?
先考慮只有兩個文件的情況下:
如果允許有一定的錯誤率,可以使用Bloom filter,4G內存大概可以表示340億bit。將其中一個文件中的url使用Bloom filter映射爲這340億bit,然後挨個讀取另外一個文件的url,檢查是否與Bloom filter,如果是,那麼該url應該是共同的url(注意會有一定的錯誤率)。”

這只是另外的解法,我想了想,如果文件多至三個或者n個,那麼還是使用分而治之/hash映射 + hash統計 + 堆/快速/歸併排序中第六題的思路吧,三個文件的時候,我們用就讓每一個小文件:a0、b0、c0。先讓a0和b0生成兩個hash_set,然後讓對c0的每一條記錄都去算hash,先a0比對,如果a0有,再b0比對。這樣三個都比對成功的就是三個文件都有的url。


第二題

在2.5億個整數中找出不重複的整數,注,內存不足以容納這2.5億個整數。
方案1:採用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)進行,共需內存2^32 * 2 bit=1 GB內存,還可以接受(爲什麼這裏取2^32,這是因爲我們假設了最大的數就是2^32,實際上,這也是各大語言中默認的int類型最大的數。所以這2.5億個數的取值範圍必須在 0< 2^32之間)。然後掃描這2.5億個整數,查看Bitmap中相對應位,如果是00變01,01變10,10保持不變。所描完事後,查看bitmap,把對應位是01的整數輸出即可。我覺得思路倒是很清晰。

方案2:也可採用與第1題類似的方法,進行劃分小文件的方法。然後在小文件中找出不重複的整數,並排序。然後再進行歸併,注意去除重複的元素。”


第三題

已知某個文件內包含一些電話號碼,每個號碼爲8位數字,統計不同號碼的個數。
8位最多99 999 999,最多需要99m個bit,那麼99 999 999/(8*1024*1024) = 11.92,所以大概10幾m字節的內存即可。 (可以理解爲從0-99 999 999的數字,每個數字對應一個Bit位,所以只需要99M個Bit==12MBytes,這樣,就用了小小的12M左右的內存表示了所有的8位數的電話)。注意只是用這個每一個bit位來表示有沒有這個電話號碼,而相應bit位的下標才表示了這個具體的電話號碼。

第四題

給40億個不重複的unsigned int的整數,沒排過序的,然後再給一個數,如何快速判斷這個數是否在那40億個數當中?
 方案1:用位圖/Bitmap的方法,那麼需要多少內存呢?因爲已經說了是unsigned int ,所以 最大的整數應該是:2^32 = 4 294 967 296。所以其給的40億數字的範圍就在 0-4 294 967 296之間。故需要 4 294 967 296 個bit位,4 294 967 296個bit就是512MB(4 294 967 296/(8*1024*1024))可得。所以申請512M的內存,一個bit位代表一個unsigned int值。讀入40億個數,設置相應的bit位,讀入要查詢的數,查看相應bit位是否爲1,爲1表示存在,爲0表示不存在。

第四五類:Trie樹/數據庫/倒排索引、外排序

有興趣的可以自行看看博客:http://blog.csdn.net/v_july_v/article/details/7382693

Trie樹

我在這裏只說一下:Trie樹。
在面對需要處理字符串的時候,不一定非要用hash來完成插入或者查找。也可以使用Trie字典樹。所以在處理字符串的時候,我們都需要想到用字典樹。字典樹又稱單詞查找樹,Trie樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希表高。可以進行查找、刪除、插入等操作。上面已經講了的題中,有如下幾個題涉及字典樹:
上面的第2題:尋找熱門查詢:查詢串的重複度比較高,雖然總數是1千萬,但如果除去重複後,不超過3百萬個,每個不超過255字節。
上面的第5題:有10個文件,每個文件1G,每個文件的每一行都存放的是用戶的query,每個文件的query都可能重複。要你按照query的頻度排序。
在分而治之/Hash映射之後,對每一份文件裏面的單詞,插入到字典樹中。在設計字典樹的數據結構的時,對於每一個節點,我們需要一個額外的一個int字段,來存儲這個單詞出現的次數。第一次插入的時候當然是1,之後每被查找到,就自加一。

第一題

1000萬字符串,其中有些是相同的(重複),需要把重複的全部去掉,保留沒有重複的字符串。請問怎麼設計和實現?
這個可能需要兩個字典樹,遇見一個插入字典數A,如果遇見在A中出現的,那麼就插入字典數B。每次遇見新的字符串時,先查找B,如果在B中出現了,那麼就不能插入A。如果B在中沒有出現,在A中也沒有,就插入A,如果B中沒有,而在A中有,那麼刪除A中有的,並插入B。

第二題

也是上面的第8題:一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞。
其解決方法是:用trie樹統計每個詞出現的次數,時間複雜度是O(n*le)(le表示單詞的平均長度),然後是找出出現最頻繁的前10個詞(使用最小堆)。


第六類:分佈式處理之Mapreduce

說起Mapreduce常常和Hadoop聯繫起啦。july總結的很好:
  1. Mapreduce是一種模式。
  2. Hadoop是一種框架。
  3. Hadoop是一個實現了mapreduce模式的開源的分佈式並行編程框架。
  4. 在hadoop的框架上採取mapreduce的模式處理海量數據。

Mapreduce

那麼下面來具體看看Mapreduce是怎樣一種模式:
所謂mapreduc是兩個單詞map和reduce的組合。Map函數把大數據集進行分解操作得到兩個或者更多的小“桶”。每臺處理器對分割出來的每個桶進行操作,獲取一組中間值,而Reduce函數是把這些中間結果通過一定的函數進行處理來獲取最終的答案。

例子一

簡單的例子就是:
假設我們有一組數據:1,2,3,…,100。求這一組數據的平方和。現在我們用MapReduce這個模型解決這一個問題。
首先我們把這組數據分成100份,交由100臺計算機去處理。每一臺計算機只做一件事,就是把自己要處理的數據平方一下。這樣一來,最初的那組數[1,2,3,…,100]就被映射成了[1,4,9,16,…,10000]了。這就是所謂的Map操作。而Reduce操作呢?Reduce操作就是把映射後得到的這100個新的數據累加。

雖然這個例子過於簡單,但是依然算是說明了問題。

例子二

下面一個例子是經常出現的,就是統計詞頻的例子,假設我們有這樣三篇文章,每篇文章的內容如下:
Paper1: We study algorithm.
Paper2: We share our thinking.
Paper3: This team shares thinking of algorithm.
我們想要統計的是所有文章的詞頻,那麼我們可以將每一篇paper交由一臺電腦計算詞頻(map),再對所有計算結果進行統計(reduce)。
所以map操作開始如下所示:

接着每臺自己計算對自己的部分進行處理,如下所示:


最後得到結果:


對其中,我唯一比較困惑的就是,對中間結果的處理,july在旁邊圖上說:還是由多臺處理器分別處理。我就很奇怪其的通信機制,或者如下


到底是存在哪裏?分別存在不同的機器上?那麼機器2 的we 1 就會跑到機器1上的文檔裏麼?對旁邊的註釋: hash%n我還是能理解的。但是我怎麼覺得只有對所有機器的文檔都遍歷一遍,邊遍歷邊算hash值再求模,映射到不同的文檔中。
其實,我覺得都不用映射都不同的文檔,直接在一份文檔裏不就好了麼?然後再進行reduce,因爲每一個文檔的每一行都是同一個字符出現的次數。所以按我的想法,上圖第C步,在一個文檔中所示才更爲合理。
  1. we 1 we 1  
  2. study 1  
  3. algorithm 1 algorithm 1  
  4. share 1 share 1  
  5. our 1  
  6. Thinking 1 Thinking 1   
  7. Team 1  
  8. of 1  
現在先做做了解吧,以後要深入學習的話,再說。

Hadoop

Hadoop既然是個框架,肯定涉及很多組件,我們來看看下面這幅圖。

下面對這幅圖的主要組件稍作解釋:
  • 分佈式文件系統(HDFS)被設計成適合運行在通用硬件(commodity hardware)上的分佈式文件系統。它和現有的分佈式文件系統有很多共同點。但同時,它和其他的分佈式文件系統的區別也是很明顯的。HDFS是一個高度容錯性的系統,適合部署在廉價的機器上。
  • HBase – Hadoop Database,首先是一個HBase是一個分佈式的、面向列的開源數據庫。作爲一個高可靠性、高性能、面向列、可伸縮的分佈式存儲系統,我們可以利用HBase技術可在廉價PC Server上搭建起大規模結構化存儲集羣。HBase利用Hadoop HDFS作爲其文件存儲系統。同時,HBase利用Hadoop MapReduce來處理HBase中的海量數據;HBase利用Zookeeper作爲協同服務。

注意,下面兩句話區分一下HDFS和Hbase(摘自:Hbase與HDFS是個什麼關係呀? http://tieba.baidu.com/p/1338652682):

  1. HDFS是一種文件格式,像FAT32,NTFS之類的,屬於底層的;HBase是數據庫,可以建在HDFS上,也可以不在這上面,不過按照設計,是推薦運行在HDFS上的。
  2. hdfs只是一個存儲空間,他的完整名字是分佈式文件系統。有名可知他的作用了。hbase是一個內存數據庫,簡單點說hbase把表啊什麼的存在hdfs上。
  • MapReduce是一種編程模型,運行分佈式系統之上。我的理解就是按照一定的模式處理着HBase或HDFS內的數據。
  • Pig是一種編程語言,它簡化了Hadoop常見的工作任務。Pig可加載數據、表達轉換數據以及存儲最終結果。Pig內置的操作使得半結構化數據變得有意義(如日誌文件)。同時Pig可擴展使用Java中添加的自定義數據類型並支持數據轉換。
  • hive是基於Hadoop的一個數據倉庫工具,可以將結構化的數據文件映射爲一張數據庫表,並提供簡單的sql查詢功能,可以將sql語句轉換爲MapReduce任務進行運行。 其優點是學習成本低,可以通過類SQL語句快速實現簡單的MapReduce統計,不必開發專門的MapReduce應用,十分適合數據倉庫的統計分析。
  • Sqoop是一個用來將Hadoop和關係型數據庫中的數據相互轉移的工具,可以將一個關係型數據庫(例如 : MySQL ,Oracle ,Postgres等)中的數據導進到Hadoop的HDFS中,也可以將HDFS的數據導進到關係型數據庫中。
總的來說,在我們編程的時候,主要是用java針對mapreduce來編程。上次參加一個講座的時候老師彷彿也說了可以利用hive,使用sql來完成同樣的工作。但是集羣的安裝配置也是值得掌握的。

後記

主要是總結了別人的,還需要更多地努力啊!加油

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