前言
本博客源自于LeeCode中的一道叫做实现前缀树的题,感觉很有意思,所以专门写一篇博客来记录下;
注意:本文的前缀树只用来简单处理字母类型的字符串;
一、前缀树是什么?什么是前缀树?
理解前缀树:
顾名思义,前缀树是一种和前缀有关的树形结构,两个关键点,前缀和树,树形结构想必都很清楚,一个节点对应多个节点的数据结构称之为树形结构;这里的前缀指字符串的一个字符,作为一个前缀存储在树形结构的节点上,一个字符串的各个字符,依次存储在树形结构的节点上所构成的树, 就是前缀树;
如下图所示:通过前缀树的方式存储了四个单词
前缀树的定义:
前缀树又名字典树,单词查找树,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
二、实现前缀树
1.基本结构
包含的元素:
- 所有子节点:
Trie[] children;
- 标志性变量(表示是否为最后一个字符):
bool is_End;
- 构造函数:初始化26个数组大小是因为在本篇博客要实现的前缀树里,每个节点最多只能包含26个子节点,也就是26个英文字母;
- 插入方法:
public void Insert(string word)
- 搜索是否已存在了某个单词:
public bool Search(string word)
- 搜索某单词是否为已存储的单词的前缀:`public bool StartsWith(string prefix)
- 私有工具方法,查找某单词的最后一个节点树:
Trie SearchPrefix(string prefix)
public class Trie
{
Trie[] children;
bool is_End;
public Trie()
{
children = new Trie[26];
is_End = false;
}
public void Insert(string word)
{
}
public bool Search(string word)
{
}
public bool StartsWith(string prefix)
{
}
Trie SearchPrefix(string prefix)
{
}
}
2.实现功能
- 实现插入方法
public void Insert(string word)
:字符串的每个字符依次存到了对应的分支节点下,如果没有该字母对应的节点,就创建一个,然后获取刚创建的子节点的引用,继续往后依次存储,直到把该字符串全部存完后,将最后一个获取到的树的结束变量设为false;到此,一个单词的树结构插入完成。
public void Insert(string word)
{
Trie node = this;
char[] chars = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
char ch = chars[i];
int index = ch - 'a';
if (node.children[index]==null)
{
node.children[index] = new Trie();
}
node = node.children[index]; //重点在这里
}
node.is_End = true;
}
- 实现私有工具方法,查找某单词的最后一个节点树:
Trie SearchPrefix(string prefix)
:以该树为根节点依次查找每个树的所有子节点,找不到就直接返回空,遍历完就返回最后一个的结果。
Trie SearchPrefix(string prefix)
{
char[] chars = prefix.ToCharArray();
Trie node = this;
for (int i = 0; i < prefix.Length; i++)
{
char ch = chars[i];
int index = ch - 'a';
if (node.children[index]==null)
{
return null;
}
node = node.children[index];
}
return node;
}
- 实现 搜索是否已存在了某个单词:
public bool Search(string word)
: 通过工具方法获取该单词在树结构内的最后一个子节点,如果该子节点不为空并且是最后一个,则存在;
public bool Search(string word)
{
Trie node = SearchPrefix(word);
return node != null && node.is_End;
}
- 实现 搜索某单词是否为已存储的单词的前缀:`public bool StartsWith(string prefix) : 不为空,则存在;
public bool StartsWith(string prefix)
{
return SearchPrefix(prefix) != null;
}
- 完整代码
public class Trie
{
Trie[] children;
bool is_End;
public Trie()
{
children = new Trie[26];
is_End = false;
}
public void Insert(string word)
{
Trie node = this;
char[] chars = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
char ch = chars[i];
int index = ch - 'a';
if (node.children[index]==null)
{
node.children[index] = new Trie();
}
node = node.children[index];
}
node.is_End = true;
}
public bool Search(string word)
{
Trie node = SearchPrefix(word);
return node != null && node.is_End;
}
public bool StartsWith(string prefix)
{
return SearchPrefix(prefix) != null;
}
Trie SearchPrefix(string prefix)
{
char[] chars = prefix.ToCharArray();
Trie node = this;
for (int i = 0; i < prefix.Length; i++)
{
char ch = chars[i];
int index = ch - 'a';
if (node.children[index]==null)
{
return null;
}
node = node.children[index];
}
return node;
}
}```