字典树TireTree的Java实现

一. 字典树能干啥

1.加快 在超长文本字符串 内 匹配大量关键词 的效率
2.快速统计 关键词 在字符串内 出现的频率或位置(进而能够找到字符串内出现频率最高的词)

比如有以下一段文本

报道称,美国海军正面临严重的战机荒。一名美国海军官员告诉众议院,通过调整航母舰载机联队的战机编制组合,到2025年缓解其攻击战斗机兵力短缺的问题。具体而言,美国海军原定每个舰载机联队配属两个F-35C中队,每个中队10架,调整后将只配属一个中队,数量增至14架。此外,美海军还将接收美空军和空中国民警卫队的部分F-16战斗机用于训练,解放部分舰载战斗机上舰部署,同时激活库存的28架F/A-18E/F。现役F/A-18E/F战斗机将进入“服役寿命改进”计划,服役寿命将由6000飞行小时延长至10000飞行小时。

现在想统计 美国美海军美国海军战斗机 这四个关键词在上文中出现的频率。

因本人太菜,所以第一反应就是 遍历关键词,拿每个关键词去文中匹配,每对应上一次,频率加一。

方式一

但是转念一想,文中出现某个关键词的地方可能很少,这样就白白扫描了很多无用的部分,而且有多少个关键词就得扫描文本多少次,花费时间 = 关键词数 × 文本长度,效率有点低。

于是马上又有了第二反应,只扫描一次文本,用文本的每个字符,匹配所有关键词的首字符,如果对应上了哪个关键词,就把该关键词频率加一。

方式二
但是转念二想,这和第一反应差不多啊,花费时间 = 文本长度 × 关键词数

难道二者真的没有区别吗?观察一会儿后,发现以文本为基础匹配关键词这种方法,如果有多个关键词的前缀是一样的,那我可不可以在匹配 美国 之后,顺道也把 美国海军 匹配了呢,相反,如果 美国 匹配失败,美国海军 必然也匹配失败,这样就能够避免匹配其他以 美国 开头的关键词,减少了扫描次数。

我们可以整合 具有相同前缀的关键词 按 由短到长 的方式排列

二. 字典树长啥样

字典树
字典树特点:

  1. 根节点啥也不存
  2. 每个节点只保存单个字符(复用关键词的相同前缀字符)
  3. 一条路径,可能包含多个关键词(根节点 → 绿色节点 = 关键词)
  4. 每个节点有 0 ~ N 个子节点(具有同一前缀字符的关键词越多,则该前缀节点的子节点越多)

三. 匹配过程

假设文本字符串 String document,字典树节点类型 TireNode;

用俩变量 int start = 0, end = 0 记录文本字符串的起始、结束索引;
一个变量 TireNode cur = root 记录待匹配的字符节点;

while ( start < document.length() )
	cur 问它的子节点们:有没有叫 document[end] 的?
		有:cur 指向该节点,end++
			如果 cur 是绿色节点,document[start, end] 就对应上了一个关键词
		无:证明 [start, end] 区间没有能够匹配的关键词啦,end = ++start,cur = root,下次准备从头匹配

流程一
流程二
流程三
流程四
流程五
流程六
流程七

四. 代码实现

1. 确定字典树节点组成

普通二叉树只有俩叉,所以可以用俩变量表示某一节点的 左、右子节点。

对于字典树的任意节点而言,它有多少子节点不确定,所以需要用集合表示,而且需要根据子节点保存的字符找到其引用,因此选用HashMap更适合。

节点属性除了保存自身的数据(字符),保存子节点们(HashMap),还应该有个关键词尾字符结束标记——是否为绿色节点。
TireNode属性

2. 代码实现节点class

class TireNode {
   
	Charater value;
	boolean tail = false;
	HashMap<Charater, TireNode> son = new HashMap<>();
	
	public TireNode(Character c){
   
		value = c;
	}
}

3. 根据所给关键词 建立字典树

美国, 美海军, 美国海军, 战斗机

因为 root 啥也不存,所以是预先初始化好的 TireNode root = new TireNode(null)

以插入关键词 美国 为例:

<1>  TireNode cur = root
<2>  if ( ! cur.son.containsKey('美') ) {
     	cur.son.put('美', new TireNode('美'))
     }
     cur = cur.son.get('美')
<3>  插入'国', 步骤同<2>
<4>  最后设置 TireNode('国').tail = true

用代码实现如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值