找top 10信息

本文章內容來源於《程序員面試寶典》。

題目:

有1千萬條短信,以文本文件的形式保存,一行一條,有重複。請用5分鐘時間,找出重複出現最多的前10條。

解析:

某些面試者想用數據庫的辦法來實現:首先將文本導入數據庫,再利用select語句某些方法得出前10條短信。但實際上用數據庫是滿足不了5分鐘解決這個條件的。這是因爲1千萬條短信即使1秒鐘錄入1萬條(這已經算是很快的數據錄入了)5分鐘才300萬條。即使真的能在5分鐘內錄入完1千萬條,也必須先建索引,不然sql語句5分鐘內肯定得不出結果。但對1千萬條記錄建索引即使在5分鐘之內都不可能完成的。所以用數據庫的辦法是不行的。類似題目是如何根據關鍵詞搜索訪問最多的前10個網站。

思路:

hash表法:可以用哈希表的方法對1千萬條分成若干組進行邊掃描邊建散列表。第一次掃描,取首字節,尾字節,中間隨便兩字節作爲Hash Code,插入到hash table中。並記錄其地址和信息長度和重複次數,1千萬條信息,記錄這幾個信息還放得下。同Hash Code且等長就疑似相同,比較一下。相同記錄只加1次進hash table,但將重複次數加1。一次掃描以後,已經記錄各自的重複次數,進行第二次hash table的處理。用線性時間選擇可在O(n)的級別上完成前10條的尋找。分組後每份中的top10必須保證各不相同,可hash來保證,也可直接按hash值的大小來分類。

排序法:可以採用從小到大排序的方法,根據經驗,除非是羣發的過節短信,否則字數越少的短信出現重複的機率越高。建議從字數少的短信開始找起,比如一開始搜一個字的短信,找出重複出現的top10並分別記錄出現次數,然後搜兩個字的,依次類推。對於對相同字數的比較長的短信的搜索,除了hash之類的算法外,可以選擇只抽取頭、中和尾等幾個位置的字符進行粗判,因爲此種判斷方式是爲了加快查找速度但未能得到真正期望的top10,因此需要做標記;如此搜索一遍後,可以從各次top10結果中找到備選的top10,如果這top10中有剛纔做過標記的,則對其對應字數的所有短信進行精確搜索以找到真正的top10並再次比較。

內存映射的方法:首先1千萬條短信按現在的短信長度將不會超過1G空間,使用內存映射文件比較合適。可以一次映射(當然如果更大的數據量的話,可以採用分段映射),由於不需要頻繁使用文件I/O和頻繁分配小內存,這將大大提高數據的加載速度。其次,對每條短信的第i(i從0到70)個字母按ASCII碼進行分組,其實也就是創建樹。i是樹的深度,也是短信第i個字母。該問題主要是解決兩方面的內容,一是內容加載,二是短信內容比較。採用文件內存映射技術可以解決內容加載的性能問題(不僅僅不需要調用文件I/O函數,而且也不需要每讀出一條短信都分配一小塊內存),而使用樹技術可以有效減少比較的次數。

代碼如下:
struct TNode
{
   BYTE *pText;
   //直接指向文件映射的內存地址
   DWORD dwCount;
   //計算器,記錄此節點的相同短信數
   TNode *ChildNodes[256];
   //子節點數據,由於一個字母的ASCII值不可能超過256,所以子節點也不可能超過256
   TNode()
   {
       //初始化成員
   }
   ~TNode()
   {
        //釋放資源
   }
};
 
//int nIndex是字母下標
void CreateChilsNode(TNode *pNode,const BYTE* pText,int nIndex)
{
    if(pNode->ChildNodes[pText[nIndex]]==NULL)
    {
        //如果不存在此子節點,就創建.TNode構造函數應該有初始化代碼
        //爲了處理方便,這裏也可以在創建的同時把此節點加到一個數組中
        pNode->ChildNodes[pText[nIndex]]=new TNode;
    }
    if(pText[nIndex+1]=='\0')
    {
          //此短信已完成,計數器加1,並保存此短信內容
          pNode->ChildNodes[pText[nIndex]]->dwCount++;
          pNode->ChildNodes[pText[nIndex]]->pText=pText;
    }
    else   //if(pText[nText]!='\0')
    {
         //如果還未結束,就創建下一級節點
         CreateNode(pNode->ChildNodes[pText[nIndex]],pText,nText+1);
    }
}

//創建根節點,pTexts是短信數組,dwCount是短信數量(這裏是1千萬)
void CreateRootNode(const BYTE **pTexts,DWOED dwCount)
{
   TNode RootNode;
   for(DWORD dwIndex=0;dwIndex<dwCount;dwIndex++)
   {
        CreateNode(&RootN,pTexts[dwIndex],0);
   }
   //所有節點按dwCount的值進行排序
   //取前10個節點,顯示結果
}


發佈了50 篇原創文章 · 獲贊 16 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章