引入
字典树是一种假想数据结构(数据结构不都是假想的吗 )
字典树又名前缀树,Trie树,是一种存储大量字符串的树形数据结构,相比于HashMap存储,在存储单词(和语种无关,任意语言都可以)的场景上,节省了大量的内存空间。
对于字典树,作为树,自然有元素之间的继承与一对多关系,每一个元素都可以有几个子元素,作为它之后的字母;
倘若要比对两个字符串是否相同,只需要比对在这棵字典树上,这两个串最后一个元素的祖先链(即前缀)是否相同,并且对于祖先链来说,并不用逐个比较,只需要记录访问就行
前缀树有什么应用吗,字符串的前缀操作处理很重要,例如百度的搜索等等等等
基本操作
初始化
class Trie {
TrieNode root;
public Trie() {
root = new TrieNode();
}
class TrieNode {
char val;
boolean isEnd = false;
TrieNode[] child = new TrieNode[26];
//数组大小跟字符类型的数量相关,我这里只考虑26小写字母
public TrieNode() {
}
public TrieNode(char val) {
this.val = val;
}
}
}
插入
//插入单词操作
public void insert(String word) {
TrieNode p = root; //创建移动节点
for (int i = 0; i < word.length(); ++i) {
char ch = word.charAt(i);
if (p.child[ch - 'a'] == null) { //判断该结点是否有过前缀
p.child[ch - 'a'] = new TrieNode(ch); //没有则创建
}
//移动到下一个节点
p = p.child[ch - 'a'];
//打上末尾字符标记
if (i == word.length() - 1) {
p.isEnd = true;
}
}
}
每个节点的所有的边都不同,这条性质可以便于我们判断在某一棵字典树到底有没有某条链:只要前缀不符合,就不需要再判断,因为必然没有(同一深度、同一父亲,边与边必定互异)
查询
查询操作,只需要一边判断是否符合要求,一边判断是否继续迭代即可。
如果在遍历过程中发现字母有缺失,即下一个字母为null,则返回false, 表示前缀树种不存在这个单词。
如果遍历完成,就要判断此时p是否为单词末尾:如果是,返回true;如果不是,说明word仅仅是一个前缀,并不完整,返回false
//查询操作
public boolean search(String word) {
TrieNode p = root;
for (int i = 0; i < word.length(); ++i) {
int c = word.charAt(i) - 'a';
if (p.child[c] == null) { //一旦发现某个字母未出现在前缀上,返回false
return false;
}
p = p.child[c];
}
return p.isEnd;
}
若要判断有无前缀,与此代码思路相同,返回值改为true即可
//有无前缀
public boolean search(String word) {
TrieNode p = root;
for (int i = 0; i < word.length(); ++i) {
int c = word.charAt(i) - 'a';
if (p.child[c] == null) { //一旦发现某个字母未出现在前缀上,返回false
return false;
}
p = p.child[c];
}
return true;
}
数组实现方式
在此附上数组实现方式(C)
基本思想不变,只是以数组实现,故在此不再赘述
#include <bits/stdc++.h>
using namespace std;
int trie[100010][26];
int idx, cnt[100010];
void insert1(char s[]) {
int p = 0;
for(int i = 0; i < strlen(s); i++) {
int u = s[i] - 'a';
if(trie[p][u] == 0) {
trie[p][u] = ++idx;
}
p = trie[p][u];
}
cnt[p]++;
}
int query(char s[]) {
int p = 0;
for(int i = 0; i < strlen(s); i++) {
int u = s[i] - 'a';
if(!trie[p][u]) {
return 0;
}
p = trie[p][u];
}
return cnt[p];
}