使用java編寫一個字符串詞頻統(tǒng)計工具
許多英語培訓(xùn)機(jī)構(gòu)(如新東方)都會出幾本“高頻詞匯”的書,主要內(nèi)容是統(tǒng)計近幾年來各類外語考試中屢次出現(xiàn)的高頻詞匯,幫助考生減少需要背的生詞的數(shù)量。但這些高頻是如何被統(tǒng)計出來的呢?顯然不會用手工去計算。
假如我們已經(jīng)將一篇文章存在一字符串(String)對象中,為了統(tǒng)計詞匯出現(xiàn)頻率,最簡單直接的做法是另外建一個Map:key是單詞,value是 次數(shù)。將文章從頭讀到尾,讀到一個單詞就到Map里查一下,如果查到了則次數(shù)加一,沒查到則往Map里一扔。
/**
* Java中提供一個非常棒的HashMap函數(shù),只需要一步就可以統(tǒng)計單詞的頻率
*/
import java.util.HashMap;
public class WordMap {
public static void main(String[] args) {
String input = "a1,b2,3c,d4d,a1,b2,a1";
String[] ws = input.split(",");
// 做的單詞分割,
// 如果你要空格分割 String[] ws = input.split(" ");
HashMap<String, Integer> hm = new HashMap<String, Integer>();
// 初始化HashMap
for (String s : ws) {
if (s.matches("[a-zA-Z]\\w+"))
// 這里是一個正則表達(dá)式,
// 判斷的是第一個必須是字母,
// 后面的是一個字母或多個字母,
// 一個數(shù)字或多個數(shù)字
{
Integer cou = hm.get(s);
// get()方法將產(chǎn)生一個與鍵相關(guān)聯(lián)的Integer值,
// 然后這個值被遞增,為的是記錄標(biāo)識符的個數(shù)
hm.put(s, cou == null ? 1 : cou + 1);
// 保存到HashMap中以單詞做key,判斷單詞是否為空,空為1,或則加1
}
}
System.out.println(hm);
// 打印HashMap
}
}這樣做雖然代碼寫起來簡單,但性能卻非常差。首先查詢Map的代價是O(logn),假設(shè)文章的字母數(shù)為m,則整個統(tǒng)計程序的時間復(fù)雜度為O(mlogn)不說,如果要拿高頻詞可能還需要對統(tǒng)計結(jié)果進(jìn)行排序。即便對結(jié)構(gòu)上進(jìn)行優(yōu)化性能仍然不高。如果能夠?qū)r間復(fù)雜度從O(mlogn)減少到O(m)的話不是更好?
為了改進(jìn)算法我們首先引進(jìn)單詞樹。與單詞前綴樹不同,單詞樹的結(jié)構(gòu)相當(dāng)簡單,結(jié)構(gòu)如圖所示:

從圖中我們可以看出,樹中每個結(jié)點(diǎn)保存屬性值cnt與指向其26個子結(jié)點(diǎn)的指針(每一條路徑代表一個英文字母),其中cnt為到達(dá)該結(jié)點(diǎn)經(jīng)過路 徑所對應(yīng)的英文單詞在文章中出現(xiàn)的次數(shù)。也就是說,我們開始讀文章時讓一個指針指向單詞數(shù)的根結(jié)點(diǎn),之后每讀一個字母就讓該指針指向當(dāng)前結(jié)點(diǎn)對應(yīng)路徑上的子結(jié)點(diǎn)(若子結(jié)點(diǎn)為空則新建一個),一個單詞讀完后讓當(dāng)前結(jié)點(diǎn)的cnt值加一,并讓指針重新指向根結(jié)點(diǎn)。而當(dāng)一篇文章讀完之后我們的單詞樹也就已經(jīng)建立完 畢了。之后只要去遍歷它并把取到的單詞根據(jù)次數(shù)進(jìn)行排序就行了(時間復(fù)雜度為O(nlogn))。
程序代碼如下,
首先是存放單詞及出現(xiàn)次數(shù)的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;
}
}其次是實現(xiàn)詞頻表生成算法的類:
public class WordCountService {
/**
* 根據(jù)文章生成單詞樹
* @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;
}
/**
* 使用深度優(yōu)先搜索遍歷單詞樹并將對應(yīng)單詞放入結(jié)果集中
* @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);
}
/**
* 得到詞頻表的主算法,供外部調(diào)用
* @param article
* @return
*/
public static List getWordCount(String article){
CharTreeNode root = geneCharTree(article);
List result = new ArrayList();//此處也可用LinkedList鏈表,以避免數(shù)組滿了擴(kuò)容導(dǎo)致的性能損失
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;
}
}
/**
* 單詞樹結(jié)點(diǎn)的定義
* @author FlameLiu
*
*/
class CharTreeNode{
int cnt = 0;
CharTreeNode[] children = new CharTreeNode[26];
}到此這篇關(guān)于使用java編寫一個字符串詞頻統(tǒng)計工具的文章就介紹到這了,更多相關(guān)java詞頻統(tǒng)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)刪除排序鏈表中的重復(fù)元素的方法
這篇文章主要介紹了Java實現(xiàn)刪除排序鏈表中的重復(fù)元素的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Java數(shù)據(jù)結(jié)構(gòu)之鏈表的增刪查改詳解
今天帶大家來學(xué)習(xí)Java鏈表的增刪改查的相關(guān)知識,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)Java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
詳解設(shè)計模式在Spring中的應(yīng)用(9種)
這篇文章主要介紹了詳解設(shè)計模式在Spring中的應(yīng)用(9種),詳細(xì)的介紹了這9種模式在項目中的應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2019-04-04
Spring Boot項目中結(jié)合MyBatis實現(xiàn)MySQL的自動主從切換功能
這篇文章主要介紹了Spring Boot項目中結(jié)合MyBatis實現(xiàn)MySQL的自動主從切換功能,本文分步驟給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-04-04
關(guān)于Spring中@Transactional事務(wù)回滾的注意事項
這篇文章主要介紹了關(guān)于Spring中@Transactional事務(wù)回滾的注意事項,回滾(Rollback)指的是程序或數(shù)據(jù)處理錯誤,將程序或數(shù)據(jù)恢復(fù)到上一次正確狀態(tài)的行為。回滾包括程序回滾和數(shù)據(jù)回滾等類型,需要的朋友可以參考下2023-05-05

