Java词频统计算法(使用单词树)

2023-11-19

许多英语培训机构(如新东方)都会出几本“高频词汇”的书,主要内容是统计近几年来各类外语考试中屡次出现的高频词汇,帮助考生减少需要背的生词的数量。但这些高频是如何被统计出来的呢?显然不会用手工去计算。


假如我们已经将一篇文章存在一字符串(String)对象中,为了统计词汇出现频率,最简单直接的做法是另外建一个Map:key是单词,value是 次数。将文章从头读到尾,读到一个单词就到Map里查一下,如果查到了则次数加一,没查到则往Map里一扔。

/**
 * Java中提供一个非常棒的HashMap函数,只需要一步就可以统计单词的频率
 */


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+"))
			// 这里是一个正则表达式,
			// 判断的是第一个必须是字母,
			// 后面的是一个字母或多个字母,
			// 一个数字或多个数字
			{
				Integer cou = hm.get(s);
				// get()方法将产生一个与键相关联的Integer值,
				// 然后这个值被递增,为的是记录标识符的个数
				hm.put(s, cou == null ? 1 : cou + 1);
				// 保存到HashMap中以单词做key,判断单词是否为空,空为1,或则加1
			}
		}
		System.out.println(hm);
		// 打印HashMap
	}
}



这样做虽然代码写起来简单,但性能却非常差。首先查询Map的代价是O(logn),假设文章的字母数为m,则整个统计程序的时间复杂度为O(mlogn)不说,如果要拿高频词可能还需要对统计结果进行排序。即便对结构上进行优化性能仍然不高。如果能够将时间复杂度从O(mlogn)减少到O(m)的话不是更好?


为了改进算法我们首先引进单词树。与单词前缀树不同,单词树的结构相当简单,结构如图所示:


从图中我们可以看出,树中每个结点保存属性值cnt与指向其26个子结点的指针(每一条路径代表一个英文字母),其中cnt为到达该结点经过路 径所对应的英文单词在文章中出现的次数。也就是说,我们开始读文章时让一个指针指向单词数的根结点,之后每读一个字母就让该指针指向当前结点对应路径上的子结点若子结点为空则新建一个),一个单词读完后让当前结点的cnt值加一并让指针重新指向根结点。而当一篇文章读完之后我们的单词树也就已经建立完 毕了。之后只要去遍历它并把取到的单词根据次数进行排序就行了(时间复杂度为O(nlogn))。

  程序代码如下,

首先是存放单词及出现次数的JavaBean

  1. 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;
        }
    }


其次是实现词频表生成算法的类:

  1. 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);
        }
        
        /**
         * 得到词频表的主算法,供外部调用
         * @param article
         * @return
         */
        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;
        }
    }
    /**
     * 单词树结点的定义
     * @author FlameLiu
     *
     */
    class CharTreeNode{
        int cnt = 0;
        CharTreeNode[] children = new CharTreeNode[26];
    }



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java词频统计算法(使用单词树) 的相关文章

随机推荐

  • Unity 使用LineRenderer连接2个物体

    1 在Hierarchy面板中创建2个GameObject A和B 这就是希望连接的2个物体 2 同理创建1个EmptyObject C 挂上LineRenderer组件 记得给Materials赋值 3 创建1个新的C 脚本LineMan
  • jenkins解决不能打印springboot启动日志问题

    背景 已经可以在jenkins打包部署 但不能显示springboot启动日志 导致springboot启动报错时 并不知道具体原因 还需要登录linux系统去查看原因 主要步骤 1 开启远程服务器日志 2 利用sed命令 结束tail命令
  • String类

    下面是关于String类1相关的知识点 关于Java JDK 中内置的一个类 java lang String 1 String表示字符串类型 属于引用数据类型 不属于基本数据类型 2 在java 中随便使用双引号括起来的都是String对
  • 计算机领域中随处可见的抽象

    想要管理多种具体的东西 那么需要遵守每种东西的规范 如果想要提供一种通用模式来对这些具体的东西统一管理 需要使用一种古老的技术 抽象 抽象是将多种具体的东西 管理时需要遵守的规范 的共同点抽取出来 放入到更高一层的抽象层 在抽象层不定义或少
  • unix环境高级编程——文件IO

    本期主题 unix环境高级编程 文件IO 文件IO 0 引言 1 文件描述符 2 IO编程中常用的API接口 1 open函数 2 close函数 3 read函数 4 write函数 5 lseek函数 3 函数sync fsync和fd
  • 51单片机中断详解(上)

    一 中断的概念 中断发生 CPU在处理某一事件A时 发生了另一事件B请求CPU迅速去处理 中断响应和中断服务 CPU暂时中断当前的工作 转去处理事件B 中断返回 待CPU将事件B处理完毕后 再回到原来事件A被中断的地方继续处理事件A 这一过
  • pgpool-II常见错误

    持续更新中 1 pg hba中的项不匹配 假设您的pgpool集群中有2个节点 您更新了其中一个节点中的pg hba conf条目 但忘记在其他节点上应用相同的条目 您会看到如下错误 psql d postgres U postgres h
  • 在Linux下编译micropython源码的方法(包括win10的ubuntu子系统)

    本文介绍了在Linux下编译micropython源码的方法 包括了虚拟机 win10子系统等 在Win10的应用商店中 提供了Linux的子系统 这是实际上是一个虚拟机软件 与virtualbox和vmplayer功能类似 下面就介绍在L
  • C语言 宏定义

    一 预备知识 一个项目可以通过 编译 链接最终形成一个可执行文件 每个源文件 cpp 都会单独编译 编译成一个目标文件 o 也可能是 obj 扩展名跟操作系统有关 然后系统把这些 o文件进行链接 最终形成一个可执行文件 编译做的事 词法 语
  • charles及弱网测试

    安装 安装完成后 charles gt help gt register 输入注册信息 Registered Name https zhile io License Key 48891cf209c6d32bf4 相关配置 1 安装根证书 h
  • 【华为OD机试】阿里巴巴找黄金宝箱(V)(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 语言限定 C clang11 C clang 11 Pascal fpc 3 0 2 Java jav
  • 跨时钟域电路设计——结绳法

    信号从快时钟域到慢时钟域过渡时 慢时钟可能无法对快时钟变化太快的信号进行采样 之前的同步器法对两个时钟间的关系有要求 结绳法适用于任何时钟域之间的过渡 结绳法的原理是将快时钟信号的脉冲周期延长 等到慢时钟周期采样后再 解绳 还原为原来的脉冲
  • Java 基础系列(十六) --- Java中模板引擎的使用

    模板引擎 1 关于动态页面的渲染 2 非模板引擎的弊端 3 模板引擎 3 1 什么是模板引擎 3 2 Thymeleaf 语法 3 3 模板引擎的使用 4 总结 1 关于动态页面的渲染 渲染就是把数据和页面进行结合起来 主要分为服务器渲染和
  • Vue3 集成 UEditor puls 富文本编辑器

    一 前端配置 1 下载代码https gitee com modstart lib ueditor plus tree master 2 解压压缩包 如下图 3 拿到dist文件夹的内容 重命名为UEditor 并将其复制到vue项目的pu
  • 未找到esUtil_win32.c 您需要查找 esUtil_win32.c 以通过查看源来确定当前调用堆栈帧

    前言 在调试OPENGL ES 3 0编程指南 原书第2版 中文版 pdf 中第8章的例子报错 在调试时 发现 esUtil win32 c文件中的代码 如下 if esMain esContext GL TRUE return 1 这里r
  • 如何零基础自学c/c++语言?

    现在零基础学习C C 无非就两种方法 一种是自学还有 一种就是报班学习 关于报班学习在这里就不多说了 那么今天就说怎么从零基础开始自学C C 编程吧 先学习C语言入门 那么问题来了 怎么去学习C语言呢 一开始肯定是要看书 这里推荐的入门书籍
  • scipy.stats的用法——常见的分布和函数

    介绍python统计函数库scipy stats中常见的分布和函数 commom distributions uniform norm poisson bernoulli expon lognorm norm t chi2 f commom
  • mysql中geometry字段的查询和保存

    有段时间为了追求效率 把用es相关的地理位置功能都去掉了 使用mysql已有的geometry的功能做地理位置 mybatis这块 TableName value t event info autoResultMap true 如果使用复杂
  • python根据excel时间表统计24小时各小时区间点的个数

    1 首先使用excel中的HOUR 函数 将日期数据 年 月 日 时 分 秒 转换为小时 表格命名为hour xlsx 2 使用python读取excel数据hour xlsx 将小时列转换为列表hour 将列表hour转换为集合myset
  • Java词频统计算法(使用单词树)

    许多英语培训机构 如新东方 都会出几本 高频词汇 的书 主要内容是统计近几年来各类外语考试中屡次出现的高频词汇 帮助考生减少需要背的生词的数量 但这些高频是如何被统计出来的呢 显然不会用手工去计算 假如我们已经将一篇文章存在一字符串 Str