Trie树
Trie树,又叫单词查找树,前缀树。是一种非常高效的储存字符串的数据结构。它可以在 O ( n ) (n为查找字符串的长度) O(n)\text{(n为查找字符串的长度)} O(n)(n为查找字符串的长度)时间复杂度内进行增删改查,非常的快。即使字符串变多,它的效率也不会降低。
实现原理
顾名思义,Trie树就是一颗树,这颗树有多少个节点?如果你存的字符串里的字符包括ASCII码,那么每个节点就有128或者256个子节点,如果你查找的字符串都是由26个小写(或者大写)字母组成,那么每个节点就有26个子节点。
我们以26子节点的单词查找树为例,它的每一个树干就代表着一个字母,从左到右依次为a-z。当你插入一个单词例如aabbc
,那么Trie树就会从根节点开始,开辟一条路径向下。比如a就会走第一条路,又是一个a再往下走第一条路,到了b就往下走第二条路,以此类推。你插入一个长度为5的单词后,Trie的树的高度就从1变成了6。然而,如果你再插入一个aac
,你会发现高度还是6,唯一不同之处就是多了一条分支c
。
root
/ | |... \
a
/ | |... \
a
/ | |... \
b c
/ | |... \
b
/ | |... \
c
需要注意的是,我们在插入一个单词后要在当前节点维护一个布尔值hasWord
,如果为true
就代表存在一个在当前位置结束的字符串。比如插入apple
,我们查找app
,这时你会发现确实存在这个节点,但很明显并没有插入这个单词,因为你查找的是某个已插入单词的前缀,当然除非你只要查找前缀。
代码模板
C++
class Trie {
private:
Trie* nodes[26] = {nullptr};
bool hasWord;
public:
Trie() {
hasWord = false;
}
void insert(string word) {
Trie* cur = this;
for (auto ch : word) {
int nex = ch - 'a';
if (!cur->nodes[nex]) cur->nodes[nex] = new Trie();
cur = cur->nodes[nex];
}
cur->hasWord = true;
}
bool search(string word) {
Trie* cur = this;
for (auto ch : word) {
cur = cur->nodes[ch - 'a'];
if (!cur) return false;
}
return cur->hasWord;
}
bool startsWith(string prefix) {
Trie* cur = this;
for (auto ch : prefix) {
cur = cur->nodes[ch - 'a'];
if (!cur) return false;
}
return true;
}
};
Java
class Trie {
private Trie[] nodes;
private boolean hasWord;
public Trie() {
nodes = new Trie[26];
hasWord = false;
}
public void insert(String word) {
Trie cur = this;
for (char ch : word.toCharArray()) {
int nex = ch - 'a';
if (cur.nodes[nex] == null) cur.nodes[nex] = new Trie();
cur = cur.nodes[nex];
}
cur.hasWord = true;
}
public boolean search(String word) {
Trie cur = this;
for (char ch : word.toCharArray()) {
cur = cur.nodes[ch - 'a'];
if (cur == null) return false;
}
return cur.hasWord;
}
public boolean startsWith(String prefix) {
Trie cur = this;
for (char ch : prefix.toCharArray()) {
cur = cur.nodes[ch - 'a'];
if (cur == null) return false;
}
return true;
}
}
Python
class Trie:
def __init__(self):
self.nodes = [None for _ in range(26)]
self.has_word = False;
def insert(self, word: str) -> None:
cur = self
for ch in word:
nex = ord(ch) - 97
if cur.nodes[nex] is None:
cur.nodes[nex] = Trie()
cur = cur.nodes[nex]
cur.has_word = True
def search(self, word: str) -> bool:
cur = self
for ch in word:
nex = ord(ch) - 97
cur = cur.nodes[nex]
if cur is None:
return False
return cur.has_word
def startsWith(self, prefix: str) -> bool:
cur = self
for ch in prefix:
nex = ord(ch) - 97
cur = cur.nodes[nex]
if cur is None:
return False
return True
nex = ord(ch) - 97
cur = cur.nodes[nex]
if cur is None:
return False
return True