Trie树,也被称为前缀树,是一种用于处理字符串的数据结构。它可以高效地进行字符串的插入、删除和搜索操作,并且能够快速找到具有相同前缀的字符串。本篇博客将详细介绍Trie树的实现原理和应用场景,并给出Java代码示例。
Trie树的基本结构由节点和边组成,每个节点表示一个字符,每条边表示一个字符的连接。从根节点到叶子节点的路径表示一个完整的字符串。
在Java代码示例中,我们定义了两种Trie树的实现方式。 Trie1
使用数组作为节点的存储结构, Trie2
使用哈希表作为节点的存储结构。两种实现方式在时间复杂度上没有本质的差异,只是在空间复杂度上有所不同。
Trie树的基本操作包括插入、删除、搜索和前缀匹配。
插入操作用于将一个字符串插入到Trie树中。具体步骤如下:
end
计数加一,表示该字符串的插入次数。删除操作用于从Trie树中删除一个字符串。具体步骤如下:
end
计数减一。pass
计数为0,表示该节点没有其他字符串经过,可以将其删除。搜索操作用于判断一个字符串在Trie树中出现的次数。具体步骤如下:
end
计数,即表示该字符串在Trie树中出现的次数。前缀匹配操作用于统计所有以给定前缀开头的字符串的出现次数。具体步骤如下:
pass
计数,即表示所有以该前缀开头的字符串的出现次数。在代码示例中,我们通过随机生成字符串数组的方式进行了多次测试,分别使用 Trie1
、 Trie2
和 Right
(正确的实现方式)进行插入、删除、搜索和前缀匹配操作,并对结果进行了比较验证。
// 示例代码
package class08;
import java.util.HashMap;
// 该程序完全正确
public class Code01_TrieTree {
public static class Node1 {
public int pass;//记录字符经过的个数
public int end;//记录字符结束的个数
public Node1[] nexts;//节点数组
// char tmp = 'b' (tmp - 'a')
public Node1() {
pass = 0;
end = 0;
nexts = new Node1[26];
}
}
public static class Trie1 {
private Node1 root;
public Trie1() {
root = new Node1();
}
public void insert(String word) {
if (word == null) {
return;
}
char[] str = word.toCharArray();
Node1 node = root;//头节点
node.pass++;//有字符经过时
int path = 0;
for (int i = 0; i < str.length; i++) { // 从左往右遍历字符
path = str[i] - 'a'; // 由字符,对应成走向哪条路
if (node.nexts[path] == null) {
node.nexts[path] = new Node1();
}
node = node.nexts[path];
node.pass++;
}
node.end++;
}
public void delete(String word) {
if (search(word) != 0) {
char[] chs = word.toCharArray();
Node1 node = root;
node.pass--;
int path = 0;
for (int i = 0; i < chs.length; i++) {
path = chs[i] - 'a';
if (--node.nexts[path].pass == 0) {
node.nexts[path] = null;
return;
}
node = node.nexts[path];
}
node.end--;
}
}
// word这个单词之前加入过几次
public int search(String word) {
if (word == null) {
return 0;
}
char[] chs = word.toCharArray();
Node1 node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.end;
}
// 所有加入的字符串中,有几个是以pre这个字符串作为前缀的
public int prefixNumber(String pre) {
if (pre == null) {
return 0;
}
char[] chs = pre.toCharArray();
Node1 node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
}
public static class Node2 {
public int pass;
public int end;
public HashMap nexts;
public Node2() {
pass = 0;
end = 0;
nexts = new HashMap<>();
}
}
public static class Trie2 {
private Node2 root;
public Trie2() {
root = new Node2();
}
public void insert(String word) {
if (word == null) {
return;
}
char[] chs = word.toCharArray();
Node2 node = root;
node.pass++;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = (int) chs[i];
if (!node.nexts.containsKey(index)) {
node.nexts.put(index, new Node2());
}
node = node.nexts.get(index);
node.pass++;
}
node.end++;
}
public void delete(String word) {
if (search(word) != 0) {
char[] chs = word.toCharArray();
Node2 node = root;
node.pass--;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = (int) chs[i];
if (--node.nexts.get(index).pass == 0) {
node.nexts.remove(index);
return;
}
node = node.nexts.get(index);
}
node.end--;
}
}
// word这个单词之前加入过几次
public int search(String word) {
if (word == null) {
return 0;
}
char[] chs = word.toCharArray();
Node2 node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = (int) chs[i];
if (!node.nexts.containsKey(index)) {
return 0;
}
node = node.nexts.get(index);
}
return node.end;
}
// 所有加入的字符串中,有几个是以pre这个字符串作为前缀的
public int prefixNumber(String pre) {
if (pre == null) {
return 0;
}
char[] chs = pre.toCharArray();
Node2 node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = (int) chs[i];
if (!node.nexts.containsKey(index)) {
return 0;
}
node = node.nexts.get(index);
}
return node.pass;
}
}
public static class Right {
private HashMap box;
public Right() {
box = new HashMap<>();
}
public void insert(String word) {
if (!box.containsKey(word)) {
box.put(word, 1);
} else {
box.put(word, box.get(word) + 1);
}
}
public void delete(String word) {
if (box.containsKey(word)) {
if (box.get(word) == 1) {
box.remove(word);
} else {
box.put(word, box.get(word) - 1);
}
}
}
public int search(String word) {
if (!box.containsKey(word)) {
return 0;
} else {
return box.get(word);
}
}
public int prefixNumber(String pre) {
int count = 0;
for (String cur : box.keySet()) {
if (cur.startsWith(pre)) {
count += box.get(cur);
}
}
return count;
}
}
// for test
public static String generateRandomString(int strLen) {
char[] ans = new char[(int) (Math.random() * strLen) + 1];
for (int i = 0; i < ans.length; i++) {
int value = (int) (Math.random() * 6);
ans[i] = (char) (97 + value);
}
return String.valueOf(ans);
}
// for test
public static String[] generateRandomStringArray(int arrLen, int strLen) {
String[] ans = new String[(int) (Math.random() * arrLen) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = generateRandomString(strLen);
}
return ans;
}
public static void main(String[] args) {
int arrLen = 100;
int strLen = 20;
int testTimes = 100000;
for (int i = 0; i < testTimes; i++) {
String[] arr = generateRandomStringArray(arrLen, strLen);
Trie1 trie1 = new Trie1();
Trie2 trie2 = new Trie2();
Right right = new Right();
for (int j = 0; j < arr.length; j++) {
double decide = Math.random();
if (decide < 0.25) {
trie1.insert(arr[j]);
trie2.insert(arr[j]);
right.insert(arr[j]);
} else if (decide < 0.5) {
trie1.delete(arr[j]);
trie2.delete(arr[j]);
right.delete(arr[j]);
} else if (decide < 0.75) {
int ans1 = trie1.search(arr[j]);
int ans2 = trie2.search(arr[j]);
int ans3 = right.search(arr[j]);
if (ans1 != ans2 || ans2 != ans3) {
System.out.println("Oops!");
}
} else {
int ans1 = trie1.prefixNumber(arr[j]);
int ans2 = trie2.prefixNumber(arr[j]);
int ans3 = right.prefixNumber(arr[j]);
if (ans1 != ans2 || ans2 != ans3) {
System.out.println("Oops!");
}
}
}
}
System.out.println("finish!");
}
}
Trie树是一种高效的字符串处理数据结构,适用于各种需要快速搜索、插入和删除字符串的场景。本篇博客介绍了Trie树的原理和实现方式,并给出了Java代码示例。希望本篇博客能够帮助读者更好地理解和应用Trie树。
以上就是对Trie树的详细介绍和应用场景的博客。希望对您有所帮助。如有任何疑问,请随时提问。