字典树
关于字典树的文章数据结构与算法面试分享(十一):前缀树(Trie Tree)这篇也写的很不错!!!
前缀树(Trie),又称字典树或单词查找树,是一种用于存储字符串集合的数据结构,它能够高效地处理与字符串相关的各种操作,如搜索、插入和删除。前缀树的核心思想是将字符串的公共前缀存储在树的节点路径上,从而实现快速检索。
节点(Node):
每个节点代表字符串中的一个字符。
节点通常包含一个字符集合,指向子节点的链接,以及一个标志位表示是否有单词在此节点结束。
根节点(Root):
根节点不包含任何字符,它是树的起始点。
路径(Path):
从根节点到某个节点的路径表示一个字符串的前缀。
单词的结束(Word Termination):
通常用一个标志位(如 isEndOfWord)标记某个节点,表示该节点是某个字符串的结束位置。
存储结构:
前缀树可以使用数组或哈希表来存储子节点的链接。
插入(Insertion):
插入操作从根节点开始,对于要插入的字符串的每个字符,如果字符对应的子节点不存在,则创建新节点。
如果字符串已经存在于树中,则更新相应的结束标志位。
搜索(Search):
搜索操作检查从根节点开始的路径是否与要搜索的字符串匹配。
如果路径上的节点都存在,则字符串存在于树中。
前缀搜索(Prefix Search):
前缀搜索查找所有以特定前缀开始的字符串
208. 实现 Trie (前缀树)
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for (int i = 0; i < prefix.length(); i++) {
char ch = prefix.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
/**
* 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);
*/
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length==0)return "";
String ans=strs[0];
for(int i=0;i<strs.length;i++){
int j=0;
for(;j<ans.length()&&j<strs[i].length();j++){
if(ans.charAt(j)!=strs[i].charAt(j))
break;
}
ans=ans.substring(0,j);
}
return ans;
}
}
648. 单词替换
利用字典树将查到的字符串前缀进行替换
class Solution {
public String replaceWords(List<String> dictionary, String sentence) {
Set<String> dictionarySet = new HashSet<String>();
for (String root : dictionary) {
dictionarySet.add(root);
}
String[] words = sentence.split(" ");
for (int i = 0; i < words.length; i++) {
String word = words[i];
for (int j = 0; j < word.length(); j++) {
if (dictionarySet.contains(word.substring(0, 1 + j))) {
words[i] = word.substring(0, 1 + j);
break;
}
}
}
return String.join(" ", words);
}
}
class Solution {
class Node {
boolean isEnd;
Node[] tns = new Node[26];
}
Node root = new Node();
void add(String s) {
Node p = root;
for (int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) p.tns[u] = new Node();
p = p.tns[u];
}
p.isEnd = true;
}
String query(String s) {
Node p = root;
for (int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (p.tns[u] == null) break;
if (p.tns[u].isEnd) return s.substring(0, i + 1);
p = p.tns[u];
}
return s;
}
public String replaceWords(List<String> ds, String s) {
for (String str : ds) add(str);
StringBuilder sb = new StringBuilder();
for (String str : s.split(" ")) sb.append(query(str)).append(" ");
return sb.substring(0, sb.length() - 1);
}
}
下面这道题好像没用到字典树的知识点啊
677. 键值映射
class MapSum {
Map<String, Integer> map;
public MapSum() {
map = new HashMap<>();
}
public void insert(String key, int val) {
map.put(key,val);
}
public int sum(String prefix) {
int res = 0;
for (String s : map.keySet()) {
if (s.startsWith(prefix)) {
res += map.get(s);
}
}
return res;
}
}
class Solution {
public String longestWord(String[] words) {
Arrays.sort(words, (a, b) -> {
if (a.length() != b.length()) {
return a.length() - b.length();
} else {
return b.compareTo(a);
}
});
String s="";
Set<String> wordss = new HashSet<String>();
wordss.add("");
int n = words.length;
for (int i = 0; i < n; i++) {
String word = words[i];
if (wordss.contains(word.substring(0, word.length() - 1))) {
wordss.add(word);
s = word;
}
}
return s;
}
}
class Solution {
public String longestWord(String[] words) {
Trie trie = new Trie();
for (String word : words) {
trie.insert(word);
}
String longest = "";
for (String word : words) {
if (trie.search(word)) {
if (word.length() > longest.length() || (word.length() == longest.length() && word.compareTo(longest) < 0)) {
longest = word;
}
}
}
return longest;
}
}
class Trie {
private boolean isEnd;
private Map<Character, Trie> children;
public Trie() {
isEnd = false;
children = new HashMap<Character, Trie>();
}
public void insert(String word) {
Trie node = this;
int length = word.length();
for (int i = 0; i < length; i++) {
char c = word.charAt(i);
node.children.putIfAbsent(c, new Trie());
node = node.children.get(c);
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = this;
int length = word.length();
for (int i = 0; i < length; i++) {
char c = word.charAt(i);
if (!node.children.containsKey(c) || !node.children.get(c).isEnd) {
return false;
}
node = node.children.get(c);
}
return node != null && node.isEnd;
}
}
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n=s.length();
boolean[] dp=new boolean[n+1];
dp[0]=true;
Set<String> set = new HashSet<>(wordDict);
for (int i = 1; i < n + 1; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && set.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[n];
}
}
class Node {
Node[] son = new Node[26];
int score;
}
class Solution {
public int[] sumPrefixScores(String[] words) {
var root = new Node();
for (var word : words) {
var cur = root;
for (var c : word.toCharArray()) {
c -= 'a';
if (cur.son[c] == null) cur.son[c] = new Node();
cur = cur.son[c];
++cur.score; // 更新所有前缀的分数
}
}
var n = words.length;
var ans = new int[n];
for (var i = 0; i < n; ++i) {
var cur = root;
for (var c : words[i].toCharArray()) {
cur = cur.son[c - 'a'];
ans[i] += cur.score; // 累加分数,即可得到答案
}
}
return ans;
}
}
后缀树
前缀树是正向插入字符的,如果说将字符串倒的插入当然就是后缀树了
820. 单词的压缩编码
class Solution {
public int minimumLengthEncoding(String[] words) {
int len = 0;
Trie trie = new Trie();
// 先对单词列表根据单词长度由长到短排序
Arrays.sort(words, (s1, s2) -> s2.length() - s1.length());
// 单词插入trie,返回该单词增加的编码长度
for (String word: words) {
len += trie.insert(word);
}
return len;
}
}
// 定义tire
class Trie {
TrieNode root;
public Trie() {
root = new TrieNode();
}
public int insert(String word) {
TrieNode cur = root;
boolean isNew = false;
// 倒着插入单词
for (int i = word.length() - 1; i >= 0; i--) {
int c = word.charAt(i) - 'a';
if (cur.children[c] == null) {
isNew = true; // 是新单词
cur.children[c] = new TrieNode();
}
cur = cur.children[c];
}
// 如果是新单词的话编码长度增加新单词的长度+1,否则不变。
return isNew? word.length() + 1: 0;
}
}
class TrieNode {
char val;
TrieNode[] children = new TrieNode[26];
public TrieNode() {}
}
还有一些字典树和DP结合的问题之后推出
你们的点赞和评论,是我更新的动力!!!