十、前缀树
1.知识梳理
-
又称为字典树,树状结构存储字典,多叉树,每个节点表示字符串的一个字符
-
字符串终止标志
-
无特殊说明,只包含小写英文字母
2.题型总结
(1)前缀树模板
-
三大基本操作:创建、插入、查找
class Trie {
static class TrieNode {//
//孩子节点
TrieNode children[];
//结尾标志
boolean isWord;
public TrieNode() {
children = new TrieNode[26];
}
}
//根节点
private TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word)
{
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (node.children[ch - 'a'] == null) {
node.children[ch - 'a'] = new TrieNode();
}
node = node.children[ch - 'a'];
} node.isWord = true;
}
public boolean search(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (node.children[ch - 'a'] == null) {
return false;
}
node = node.children[ch - 'a'];
}
return node.isWord;
}
public boolean startsWith(String prefix) {
TrieNode node = root;
for (char ch : prefix.toCharArray()) {
if (node.children[ch - 'a'] == null) {
return false;
}
node = node.children[ch - 'a'];
}
return true;
}
}
1、构建字典树
2、逐个转换
class Solution {
public String replaceWords(List<String> dictionary, String sentence) {
}
}
(2)前缀树+DFS
class MagicDictionary {
static class TrieNode {
TrieNode children[];
boolean isWord;
public TrieNode() {
children = new TrieNode[26];
}
}
TrieNode root;
public MagicDictionary() {
root = new TrieNode();
}
public void buildDict(String[] dictionary) {
for (String word : dictionary) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (node.children[ch - 'a'] == null) {
node.children[ch - 'a'] = new TrieNode();
}
node = node.children[ch - 'a'];
}
node.isWord = true;
}
}
public boolean search(String searchWord) {
return dfs(root, searchWord, 0, 0);
}
private boolean dfs(TrieNode root, String searchWord, int i, int flag) {
if (root == null) {
return false;
}
if (root.isWord && i == searchWord.length() && flag == 1) {
return true;
}
if (i < searchWord.length() && flag <= 1) {
boolean found = false;
for (int j = 0; j < 26 && !found; j++) {
int nextFlag = (j == searchWord.charAt(i) - 'a') ? flag : flag + 1;
found = dfs(root.children[j], searchWord, i + 1, nextFlag);
}
return found;
}
return false;
}
}
/**
* Your MagicDictionary object will be instantiated and called as such:
* MagicDictionary obj = new MagicDictionary();
* obj.buildDict(dictionary);
* boolean param_2 = obj.search(searchWord);
*/
先构造字典树(倒序添加),然后dfs计算每条到达叶子节点路径长的总和
class Solution {
//构造前缀树
class TrieNode {
public TrieNode[] children;
public TrieNode() {
children = new TrieNode[26];
}
}
public int minimumLengthEncoding(String[] words) {
TrieNode root = buildTrie(words);
int[] total = {0};
dfs(root, 1, total);
return total[0];
}
private void dfs(TrieNode root, int length, int[] total) {
boolean isLeaf = true;
for (TrieNode child : root.children) {
if (child != null) {
isLeaf = false;
dfs(child, length + 1, total);
}
}
if (isLeaf) {
total[0] += length;
}
}
//构造前缀树,注意从每个单词的最后一个字符倒过来往前存储
private TrieNode buildTrie(String[] words) {
TrieNode root = new TrieNode();
for (String word : words) {
TrieNode curr = root;
for (int i = word.length() - 1; i >= 0; i--) {
char ch = word.charAt(i);
if (curr.children[ch - 'a'] == null) {
curr.children[ch - 'a'] = new TrieNode();
}
curr = curr.children[ch - 'a'];
}
}
return root;
}
}
插入操作构造字典树,然后找到前缀末尾,开始递归调用求和函数
class MapSum {
class TrieNode {
TrieNode children[];
int value;
public TrieNode() {
children = new TrieNode[26];
}
}
private TrieNode root;
public MapSum() {
root = new TrieNode();
}
public void insert(String key, int val) {
TrieNode curr = root;
for (char ch : key.toCharArray()) {
if (curr.children[ch - 'a'] == null) {
curr.children[ch - 'a'] = new TrieNode();
}
curr = curr.children[ch - 'a'];
}
curr.value = val;
}
public int sum(String prefix) {
//先判断有没有prefix的
TrieNode curr = root;
for (char ch : prefix.toCharArray()) {
if (curr.children[ch - 'a'] == null) {
return 0;
}
curr = curr.children[ch - 'a'];
}
return getSum(curr);
}
private int getSum(TrieNode curr) {
if (curr == null) return 0;
int ans = curr.value;
for (TrieNode child : curr.children) {
ans += getSum(child);
}
return ans;
}
}
/**
* Your MapSum object will be instantiated and called as such:
* MapSum obj = new MapSum();
* obj.insert(key,val);
* int param_2 = obj.sum(prefix);
*/
(3)前缀树+遍历
先构造字典树,然后遍历每个数,进行查找,优先选择与当前位相反的节点,最后选择最大值即可
class Solution {
static class TrieNode {
TrieNode children[];
public TrieNode() {
children = new TrieNode[2];
}
}
// private TrieNode root;
public int findMaximumXOR(int[] nums) {
TrieNode root = buildTrie(nums);
int max = 0;
for (int num : nums) {
TrieNode curr = root;
int xor = 0;
for (int i = 31; i >= 0; i--) {
int bit = (num >> i) & 1;
if (curr.children[1 - bit] == null) {
xor = xor << 1;
curr = curr.children[bit];
} else {
xor = (xor << 1) + 1;
curr = curr.children[1 - bit];
}
}
max = Math.max(max, xor);
}
return max;
}
private TrieNode buildTrie(int[] nums) {
TrieNode root = new TrieNode();
for (int num : nums) {
TrieNode curr = root;
for (int i = 31; i >= 0; i--) {
int bit = (num >> i) & 1;
if (curr.children[bit] == null) {
curr.children[bit] = new TrieNode();
}
curr = curr.children[bit];
}
}
return root;
}
}