字典树是一种重要的数据结构,常常用于处理大量的字符串,在数据存储以及查询方面都具有优势。下面一起来看看吧:




字典树的定义与理解

定义和特点:

字典树,有时也称为单词查找树、前缀树或者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 -


算法专题 之 字典树_回文串