使用單詞樹進行詞頻統計算法

 

許多英語培訓機構(如新東方)都會出幾本“高頻詞彙”的書,主要內容是統計近幾年來各類外語考試中屢次出現的高頻詞彙,幫助考生減少需要背的生詞的數量。但這些高頻是如何被統計出來的呢?顯然不會用手工去計算。
  假如我們已經將一篇文章存在一字符串(String)對象中,爲了統計詞彙出現頻率,最簡單直接的做法是另外建一個Map:key是單詞,value是次數。將文章從頭讀到尾,讀到一個單詞就到Map裏查一下,如果查到了則次數加一,沒查到則往Map裏一扔。這樣做雖然代碼寫起來簡單,但性能卻非常差。首先查詢Map的代價是O(logn),假設文章的字母數爲m,則整個統計程序的時間複雜度爲O(mlogn)不說,如果要拿高頻詞可能還需要對統計結果進行排序。即便對結構上進行優化性能仍然不高。如果能夠將時間複雜度從O(mlogn)減少到O(m)的話不是更好?
  爲了改進算法我們首先引進單詞樹。與單詞前綴樹不同,單詞樹的結構相當簡單,結構如圖所示:

從圖中我們可以看出,樹中每個結點保存屬性值cnt與指向其26個子結點的指針(每一條路徑代表一個英文字母),其中cnt爲到達該結點經過路徑所對應的英文單詞在文章中出現的次數。也就是說,我們開始讀文章時讓一個指針指向單詞數的根結點,之後每度一個字母就讓該指針指向當前結點對應路徑上的子結點(若子結點爲空則新建一個),一個單詞讀完後讓當前結點的cnt值加一,並讓指針重新指向根結點。而當一篇文章讀完之後我們的單詞樹也就已經建立完畢了。之後只要去遍歷它並把取到的單詞根據次數進行排序就行了(時間複雜度爲O(nlogn))。

  程序代碼如下,首先是存放單詞及出現次數的JavaBean

public class WordCount {
   private String word;
   
   private int count;
   public int getCount() {
       return count;
    }
  public void setCount(int count) {
       this.count = count;
   }
    public String getWord() {
      return word;
   }
   public void setWord(String word) {
       this.word = word;
    }
}
  
//其次是實現詞頻表生成算法的類:

public class WordCountService {
   
   /**
   * 根據文章生成單詞樹
    * @param text
    * @return
    */
	private static CharTreeNode geneCharTree(String text){
       CharTreeNode root = new CharTreeNode();
        CharTreeNode p = root;
       char c = ' ';
       for(int i = 0; i < text.length(); ++i){
           c = text.charAt(i);
           if(c >= 'A' && c <= 'Z')
               c = (char)(c + 'a' - 'A');
           if(c >= 'a' && c <= 'z'){
               if(p.children[c-'a'] == null)
                    p.children[c-'a'] = new CharTreeNode();
              p = p.children[c-'a'];
           }
            else{
               p.cnt ++;
               p = root;
           }
       }
       if(c >= 'a' && c <= 'z')
          p.cnt ++;
       return root;
   }
   
   /**
    * 使用深度優先搜索遍歷單詞樹並將對應單詞放入結果集中
     * @param result
     * @param p
     * @param buffer
     * @param length
     */
	private static void getWordCountFromCharTree(List result,CharTreeNode p, char[] buffer, int length){
        for(int i = 0; i < 26; ++i){
           if(p.children[i] != null){
               buffer[length] = (char)(i + 'a');
                if(p.children[i].cnt > 0){
                    WordCount wc = new WordCount();
                    wc.setCount(p.children[i].cnt);              
                  wc.setWord(String.valueOf(buffer, 0, length+1));
                  result.add(wc);
               }
               getWordCountFromCharTree(result,p.children[i],buffer,length+1);
           }
     }
   }
   
   private static void getWordCountFromCharTree(List result,CharTreeNode p){
        getWordCountFromCharTree(result,p,new char[100],0);
   }
    
     /**
      * 得到詞頻表的主算法,供外部調用
         */
 public static List getWordCount(String article){
     CharTreeNode root = geneCharTree(article);
      List result = new ArrayList();//此處也可用LinkedList鏈表,以避免數組滿了擴容導致的性能損失 
      getWordCountFromCharTree(result,root);
      Collections.sort(result, new Comparator(){
          public int compare(Object o1, Object o2) {
             WordCount wc1 = (WordCount)o1;
            WordCount wc2 = (WordCount)o2;
            return wc2.getCount() - wc1.getCount();
 }
});
return result;
}
}
/**單詞樹結點的定義*/

class CharTreeNode{
  int cnt = 0;
    CharTreeNode[] children = new CharTreeNode[26];
}






 

 


 

 

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