字典树是一种重要的数据结构,常常用于处理大量的字符串,在数据存储以及查询方面都具有优势。下面一起来看看吧:
字典树的定义与理解
定义和特点:
字典树,有时也称为单词查找树、前缀树或者Tire数树,是一种哈希树的变种。字典树是一种树形结构,英文字母的字典树是26叉数,数字的字典树是10叉树,利用了字符串的公共前缀,在存储时能够节约存储空间,在查询时能够最大限度的减少字符串的比较。
适用场景:
(1) 充分利用公共前缀,节约空间且存好后字符串是有序的,不仅适合大量字符串的存储,而且适合大量字符串的排序;
(2) 字典树对存入的字符串已经排序,因此还适合快速查询字符串以及判断某字符串是否存在。
(3) 用于统计,字典树经常被用于搜索引擎系统用于文本词频的统计。
基本性质:
(1) 根节点不包含字符(便于插入和查找),根节点外每一个节点都只包含一个字符;
(2) 字典从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串;
(3) 每个节点的所有子节点包含的字符都不相同。
实例解析
题目1:实现字典树的常用场景
题目描述:实现字典树的存储,查询和统计三种场景。
注:下面代码可左右滑动查看
public class TrieTree {
TrieNode root = new TrieNode();
//在字典树中插入字符串
public void insert(String str){
if (str == null || str.length() == 0) {
return;
}
TrieNode node = root;
char[] letters = str.toCharArray();
for (int i = 0, len = str.length(); i < len; i++) {
int pos = letters[i] - 'a';
if (node.child[pos] == null) {
node.child[pos] = new TrieNode();
node.child[pos].val = letters[i];
} else {
node.child[pos].count++;
}
node = node.child[pos];
}
node.isEnd = true;
}
//在字典树中查找指定字符串
public boolean searchStr(String str){
if(str == null|| str.length() == 0) {
return false;
}
TrieNode node = root;
char[] letters = str.toCharArray();
for(int i = 0; i < str.length(); i++) {
int pos = letters[i] - 'a';
if(node.child[pos] != null) {
node = node.child[pos];
}else {
return false;
}
}
return node.isEnd;
}
//统计前缀字符串的数量
public int countPrefix(String prefix) {
if(prefix == null || prefix.length() == 0) {
return - 1;
}
TrieNode node = root;
char[] letters = prefix.toCharArray();
for(int i = 0; i < prefix.length(); i++) {
int pos = letters[i] - 'a';
if(node.child[pos] == null) {
return 0;
} else {
node = node.child[pos];
}
}
return node.count;
}
}
class TrieNode {
public int count; // 统计有多少单词通过这个节点
public TrieNode[] child; // 儿子节点
public boolean isEnd; // 判断是否是最后一个节点
public char val; // 节点的值
private final int SIZE = 26; // 英文字母的字典树是26叉数,数字的字典树是10叉树
TrieNode() {
count = 1;
child = new TrieNode[SIZE];
isEnd = false;
}
}
题目2:回文对(leetcode难度系数:困难)
题目描述:给定一组唯一的单词, 找出所有不同的索引对(i, j),使得列表中的两个单词, words[i] + words[j] ,可拼接成回文串。
示例1:
输入: ["abcd","dcba","lls","s","sssll"];
输出: [[0,1],[1,0],[3,2],[2,4]] ;
解释: 可拼接成的回文串为 ["dcbaabcd","abcddcba","slls","llssssll"]
示例2:
输入: ["bat","tab","cat"];
输出: [[0,1],[1,0]] ;
解释: 可拼接成的回文串为 ["battab","tabbat"]
注:下面代码可左右滑动查看
public class palindromePairs{
private class TrieNode {
TrieNode[] next;
int index;
List<Integer> list;
TrieNode() {
next = new TrieNode[26];
index = -1;
list = new ArrayList<>();
}
}
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> res = new ArrayList<>();
TrieNode root = new TrieNode();
for (int i = 0; i < words.length; i++) {
addWord(root, words[i], i);
}
for (int i = 0; i < words.length; i++) {
search(words, i, root, res);
}
return res;
}
private void addWord(TrieNode root, String word, int index) {
for (int i = word.length() - 1; i >= 0; i--) {
int j = word.charAt(i) - 'a';
if (root.next[j] == null) {
root.next[j] = new TrieNode();
}
if (isPalindrome(word, 0, i)) {
root.list.add(index);
}
root = root.next[j];
}
root.list.add(index);
root.index = index;
}
private void search(String[] words, int i, TrieNode root, List<List<Integer>> res) {
for (int j = 0; j < words[i].length(); j++) {
if (root.index >= 0 && root.index != i && isPalindrome(words[i], j, words[i].length() - 1)) {
res.add(Arrays.asList(i, root.index));
}
root = root.next[words[i].charAt(j) - 'a'];
if (root == null) return;
}
for (int j : root.list) {
if (i == j) continue;
res.add(Arrays.asList(i, j));
}
}
private boolean isPalindrome(String word, int i, int j) {
while (i < j) {
if (word.charAt(i++) != word.charAt(j--)) return false;
}
return true;
}
}
- END -