时间复杂度与数据量无关,与样本长度有关。
- 每个节点保存三个数据:
path
: 有多少节点滑过
end
: 多少节点以当前字符结束
TrieNode[] nexts
: 链接数组,链接的索引隐式地定义了对应地字符。
链接数组保存了下一个节点的链接。
JAVA代码
public class TrieTree {
/***************定义节点结构*******************/
public static class TrieNode{
public int path;
public int end;
public TrieNode[] nexts;
public TrieNode(){
path = 0; //扫过该字母的次数:用于求相同前缀的单词数量
end = 0; //以该字母结束的单词数:用于求单词数量/单词是否存在/删除单词
nexts = new TrieNode[26];
}
}
public static class Trie{
private TrieNode root;
public Trie(){
root = new TrieNode();
}
/********************插入单词********************/
public void insert(String word){ //插入单词
if (word==null) return;
TrieNode node = root;
char[] chs = word.toCharArray();
for (int i=0; i<chs.length; i++){
int index = chs[i]-'a';
if (node.nexts[index] ==null ){
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
node.path++;
}
node.end++;
}
/******************查找某个单词出现过多少次*********************/
public int search(String word){ //找有几个该单词
if (word == null) return 0;
TrieNode node = root;
char[] chs = word.toCharArray();
for (int i=0; i<chs.length; i++){
int index = chs[i]-'a';
if (node.nexts[index] != null){
node = node.nexts[index];
}else{
return 0;
}
}
return node.end;
}
/****************删除单词****************/
public void delete(String word){
if (search(word)!=0) { //确定树中存在该单词
TrieNode node = root;
char[] chs = word.toCharArray();
for (int i = 0; i < chs.length; i++) {
int index = chs[i] - 'a';
if (--node.nexts[index].path == 0) { //path减到0了,后面的链接直接删除
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
/*********************某相同前缀的单词有多少个**********************/
public int prefixNumber(String pre){
int count = 0;
if (pre == null) return count;
TrieNode node = root;
char[] chs = pre.toCharArray();
for (int i=0; i<chs.length; i++){
int index = chs[i] - 'a';
if (node.nexts[index]==null){ //不存在该前缀,返回0
return 0;
}
node = node.nexts[index];
}
return node.path;
}
}
}
leetcode 208. 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 true
trie.search("app"); // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");
trie.search("app"); // 返回 true
题解:
前缀树结构。
只有插入、查找、找前缀。因此可如下设置前缀树节点结构:
boolean end
: 是否有单词以当前字符结束
TrieNode[] nexts
: 链接数组。
JAVA代码
class Trie {
/** Initialize your data structure here. */
//定义前缀树节点结构
public static class TrieNode{
public boolean end;
public TrieNode[] nexts;
public TrieNode(){
end = false;
nexts = new TrieNode[26];
}
}
TrieNode root;
public Trie() {
root = new TrieNode();
}
/** Inserts a word into the trie. */
public void insert(String word) {
if (word == null) return;
TrieNode node = root;
char[] chs = word.toCharArray();
for (int i = 0; i< chs.length; i++){
int index = chs[i]-'a';
if (node.nexts[index] == null){
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
}
node.end = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
if (word == null) return false;
TrieNode node = root;
char[] chs = word.toCharArray();
for (int i=0; i<chs.length; i++){
int index = chs[i]-'a';
if (node.nexts[index] == null){
return false;
}
node = node.nexts[index];
}
if (node.end == false) return false;
return true;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
if (prefix == null) return false;
TrieNode node = root;
char[] chs = prefix.toCharArray();
for(int i=0; i<chs.length; i++){
int index = chs[i] - 'a';
if (node.nexts[index] == null){
return false;
}
node = node.nexts[index];
}
return true;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/
677. 键值映射
实现一个 MapSum 类里的两个方法,insert 和 sum。
对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。
对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
示例 1:
输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5
题解:
class MapSum {
/** Initialize your data structure here. */
public static class TrieNode{
public int val;
public TrieNode[] nexts;
public TrieNode(){
val = 0;
nexts = new TrieNode[26];
}
}
TrieNode root;
public MapSum() {
root = new TrieNode();
}
public void insert(String key, int val) {
if (key == null) return;
char[] chs = key.toCharArray();
TrieNode node = root;
for (int i=0; i<chs.length; i++){
int index = chs[i]-'a';
//若不存在该单词,创建新的节点路径
if (node.nexts[index]==null){
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
}
//下面两句包含两层含义:
//1. 若不存在该单词,直接创建一个新的路径
//2. 若已存在该单词,原来的键值替代成新的键值
node.val = val;
}
public int sum(String prefix) {
int num = 0;
if (prefix == null) return 0;
char[] chs = prefix.toCharArray();
TrieNode node = root;
for (int i=0; i<chs.length;i++){
int index = chs[i] - 'a';
if(node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return dfs(node);
}
//深度优先遍历当前节点后面的所有TrieNode, 累加val值
public int dfs(TrieNode node){
int num = 0;
if (node==null) return num;
num += node.val;
for (TrieNode curr: node.nexts){
num += dfs(curr);
}
return num;
}
}