单词查找树
本篇笔记基于Java语言,是《算法 第四版》的读书笔记,为了适当简化,部分内容和原书有出入。
单词查找树和符号表差不多,符号表允许存入任意类型的键值对(当然Key是要实现了Comparable接口的)。符号表可以用二叉查找树实现。
单词查找树差不多,只不过它的键被固定为String
类型。它用字符串的特性来实现高效的字符查找树功能,虽然Java的字符串实现让字符串可以以极高效的速度进行比较,但是在一些其它语言里,实现单词查找树还是很有必要的。
字符表#
我们先来介绍一下字符表的概念,字符表其实就是一堆字符的有序集合。它有如下属性:
- 字符集: 字符表中包含的所有字符
- 基数: 字符表中包含多少字符
而且因为它是有序的,所以可以根据下标获取字符表对应位置的字符,也可以把字符映射成对应位置的下标。
所以我们可以定义一些字符表:
名称 | 基数 | 字符集 | 备注 |
---|---|---|---|
BINARY | 2 | 01 | 二进制数字 |
DNA | 4 | ACTG | DNA碱基符号 |
OCTAL | 8 | 01234567 | 八进制数字 |
ASCII | 128 | 略 | 标准ASCII字符集 |
EXTENDED_ASCII | 256 | 略 | 扩展ASCII字符集 |
UNICODE16 | 65535 | 略 | Unicode字符集 |
有了字符表,我们就可以把具体的字符串问题限制在应用使用的字符表上了。
API#
看看单词查找树的API
public class StringST |
备注 |
---|---|
void put(String key,Value value) | 向表中插入键值对(值为null则删除key) |
Value get(String key) | 获取键key所对应的值 |
void delete(String key) | 删除键Key和对应值 |
boolean contains(String key) | 表中是否包含键Key |
boolean isEmpty() | 表是否为空 |
String longestPrefixOf(String s) | s的前缀中最长的键 |
Iterable |
所有以s为前缀的键 |
Iterable |
所有和s匹配的键(“.”匹配任意字符) |
int size() | 键值对的数量 |
Iterable |
符号表中所有键 |
如何构造单词查找树#
假设我们用DNA碱基符号作为字符表来画一张图说明单词查找树的工作过程。
DNA碱基符号的R为4
假设我们有一个DNA序列的键ACGA
,那么它将这样存储在单词查找树中。
这图画得有些不形象。单词查找树中每一个节点有R个子节点,每个子节点又会连接R个子节点。因为字符表是有序的,可以将字符映射成在字符表中的下标,所以每次向下查找需要画O(1)的时间,查找的总时间和字符串长度是线性关系O(n)。
一个约定#
单词查找树使用扩展ASCII字符表,R为256,并且,如果直接将Java中的char
类型数据当作int
类型使用,那么它就是在字符表中的下标。
put和get#
public class TrieST<Value>{
// 基数
private static int R = 256;
private int length = 0;
private Node root = new Node();
public TrieST(){}
public TrieST(int R){
this.R = R;
}
private static class Node{
private Object value;
private Node[] childs = new Node[R];
}
public Value get(String key){
Node n = get(root,0,key);
if (n == null) return null;
return (Value) n.value;
}
public Node get(Node node,int d,String key){
if (node == null)return null;
if (d == key.length()) return node;
return get(node.childs[key.charAt(d)],d+1,key);
}
public void put(String key,Value value){
put(root,0,key,value);
length++;
}
public void put(Node node,int d,String key,Value value){
Node next = node.childs[key.charAt(d)];
if (next == null){
next = new Node();
node.childs[key.charAt(d)] = next;
}
if (d == key.length() - 1)
next.value = value;
else if (d < key.length() - 1)
put(next,d+1,key,value);
}
public int length(){return length;}
}
测试用例:
public class TrieSTTest {
public static void main(String[] args) {
TrieST<Integer> trie = new TrieST();
System.out.println(trie.get("shells"));
trie.put("she",1);
trie.put("sells",2);
trie.put("sea",3);
trie.put("shells",4);
trie.put("by",5);
trie.put("the",6);
trie.put("shore",7);
System.out.println(trie.get("shells"));
System.out.println(trie.get("sea"));
System.out.println(trie.get("by"));
}
}
keys()#
public Iterable<String> keys(){
return keyWithPrefix("");
}
public Iterable<String> keyWithPrefix(String prefix){
LinkedList<String> queue = new LinkedList<>();
collect(get(root,0,prefix),prefix,queue);
return queue;
}
private void collect(Node x, String pre, LinkedList<String> q){
if (x==null) return;
if (x.value != null) q.addLast(pre);
for (char c = 0;c<R;c++)
collect(x.childs[c],pre+c,q);
}
暂时实现这么多...
作者:Yudoge
出处:https://www.cnblogs.com/lilpig/p/12341181.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
欢迎按协议规定转载,方便的话,发个站内信给我嗷~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)