字典树(Trie),也被称为前缀树或单词查找树,是一种用于存储字符串集合的树形数据结构。字典树的每个节点通常表示一个字符,而整个路径则代表一个单词。字典树的主要优势在于它能够高效地处理字符串的前缀查询、后缀查询以及包含查询等操作。
字典树的特点:
- 高效字符串操作:字典树可以快速地进行字符串的查找、插入和删除操作。
- 节省空间:对于具有共同前缀的字符串,字典树通过共享相同的前缀节点来节省内存空间。
- 支持多种查询:除了基本的查找、插入和删除操作,字典树还支持前缀查询、后缀查询等。
字典树的基本操作:
- 插入(Insert):将一个字符串插入字典树中,从根节点开始,根据字符串的每个字符沿着树向下查找或创建节点,直到字符串的末尾。
- 查找(Search):在字典树中查找一个字符串,从根节点开始,沿着树向下查找,如果字符串的每个字符都能在树中找到,并且顺序正确,则该字符串存在于字典树中。
- 删除(Delete):从字典树中删除一个字符串,需要递归地删除每个字符节点,并在删除过程中处理那些没有子节点的节点。
字典树的Java实现:
class TrieNode {
private TrieNode[] children;
private boolean isEndOfWord;
public TrieNode() {
children = new TrieNode[26]; // 假设只处理小写字母
isEndOfWord = false;
}
public boolean containsKey(char ch) {
return children[ch - 'a'] != null;
}
public TrieNode get(char ch) {
return children[ch - 'a'];
}
public void put(char ch, TrieNode node) {
children[ch - 'a'] = node;
}
public void setEndOfWord() {
isEndOfWord = true;
}
public boolean isEndOfWord() {
return isEndOfWord;
}
}
public class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode node = root;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (!node.containsKey(ch)) {
node.put(ch, new TrieNode());
}
node = node.get(ch);
}
node.setEndOfWord();
}
public boolean search(String word) {
TrieNode node = root;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (!node.containsKey(ch)) {
return false;
}
node = node.get(ch);
}
return node.isEndOfWord();
}
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("apple");
trie.insert("app");
System.out.println("Search apple: " + trie.search("apple")); // true
System.out.println("Search app: " + trie.search("app")); // true
}
}
字典树的应用场景:
- 自动补全:在搜索引擎或文本编辑器中实现自动补全功能。
- 拼写检查:提供拼写错误的单词建议。
- IP 路由:在网络中进行高效的IP路由查找。
- 词频统计:统计文本中单词的出现频率。
面试大厂题示例:
-
实现一个单词建议系统:
描述:给定一个单词列表和用户的部分输入,返回一个建议单词列表,这些单词必须以用户输入的前缀开头。
示例:输入: words = ["apple", "app", "banana", "orange", "application", "apply"], prefix = "ap" 输出: ["apple", "app", "application"]
-
单词接龙:
描述:给定两个单词列表,判断是否存在一个单词接龙,即第一个列表中的单词可以通过改变一个字母变成第二个列表中的某个单词。
示例:输入: list1 = ["apple", "application", "apply"], list2 = ["app", "pine", "pineapple"] 输出: true
-
统计不同子串的数量:
描述:给定一个字符串和一个单词列表,返回在字符串中单词列表中单词作为子串出现的次数。
示例:输入: s = "abab", words = ["ab", "ba", "abab"] 输出: 2
这些题目和源码展示了字典树在解决实际问题中的应用。在面试中,能够根据问题的特点选择合适的算法并实现其解决方案是非常重要的。希望这些示例能够帮助你更好地准备面试!
字典树(Trie)是一种高效的字符串处理数据结构,适用于解决与字符串相关的问题。以下是三道可能出现在大厂面试中的与字典树相关的编程题目,以及相应的Java源码实现。
题目 1:实现一个单词建议器
描述:
实现一个单词建议器,给定一个单词列表和一个前缀,返回所有以该前缀开头的单词。
示例:
输入: words = ["apple", "app", "banana", "orange", "application", "apply"], prefix = "ap"
输出: ["apple", "app", "application"]
Java 源码:
public class WordSuggester {
private TrieNode root;
public WordSuggester() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (!node.containsKey(ch)) {
node.put(ch, new TrieNode());
}
node = node.get(ch);
}
node.setEndOfWord();
}
public List<String> suggest(String prefix) {
List<String> suggestions = new ArrayList<>();
TrieNode node = root;
for (char ch : prefix.toCharArray()) {
node = node.get(ch);
if (node == null) {
return suggestions;
}
}
collectWords(node, new StringBuilder(prefix), suggestions);
return suggestions;
}
private void collectWords(TrieNode node, StringBuilder path, List<String> suggestions) {
if (node.isEndOfWord()) {
suggestions.add(path.toString());
}
for (char ch = 'a'; ch <= 'z'; ch++) {
if (node.containsKey(ch)) {
path.append(ch);
collectWords(node.get(ch), path, suggestions);
path.deleteCharAt(path.length() - 1);
}
}
}
public static void main(String[] args) {
WordSuggester suggester = new WordSuggester();
suggester.insert("apple");
suggester.insert("app");
suggester.insert("banana");
suggester.insert("orange");
suggester.insert("application");
suggester.insert("apply");
List<String> suggestions = suggester.suggest("ap");
System.out.println("Suggestions: " + suggestions);
}
}
题目 2:实现一个字符串搜索算法
描述:
实现一个字符串搜索算法,给定一个文本和一个模式,找到所有模式在文本中的出现位置。
示例:
输入: text = "ababcabcabc", pattern = "abc"
输出: [0, 4, 5]
Java 源码:
public class StringSearch {
public List<Integer> search(String text, String pattern) {
List<Integer> matches = new ArrayList<>();
int n = text.length(), m = pattern.length();
TrieNode root = buildTrie(pattern);
for (int i = 0; i <= n - m; i++) {
int j = 0;
TrieNode node = root;
for (; j < m; j++) {
char ch = text.charAt(i + j);
if (!node.containsKey(ch)) {
break;
}
node = node.get(ch);
}
if (j == m) {
matches.add(i);
}
}
return matches;
}
private TrieNode buildTrie(String pattern) {
TrieNode root = new TrieNode();
for (char ch : pattern.toCharArray()) {
root = root.put(ch, new TrieNode());
}
root.setEndOfWord();
return root;
}
public static void main(String[] args) {
StringSearch search = new StringSearch();
List<Integer> matches = search("ababcabcabc", "abc");
System.out.println("Pattern positions: " + matches);
}
}
题目 3:实现一个单词拼写检查器
描述:
实现一个单词拼写检查器,给定一个单词列表和一个单词,判断该单词是否在单词列表中,并返回一组最接近的单词。
示例:
输入: wordList = ["apple", "bat", "cat", "fit", "fat"], word = "hat"
输出: ["cat", "bat", "hat", "fit"]
Java 源码:
public class SpellChecker {
private TrieNode root;
private Set<String> wordSet;
public SpellChecker(String[] wordList) {
root = new TrieNode();
wordSet = new HashSet<>();
for (String word : wordList) {
insert(word);
wordSet.add(word);
}
}
public boolean isWord(String word) {
return search(root, word).isEndOfWord();
}
public List<String> suggest(String word) {
List<String> suggestions = new ArrayList<>();
TrieNode node = root;
for (char ch : word.toCharArray()) {
node = node.get(ch);
if (node == null) {
return suggestions;
}
}
collectClosestWords(node, new StringBuilder(word), wordSet, suggestions);
return suggestions;
}
private void collectClosestWords(TrieNode node, StringBuilder path, Set<String> wordSet, List<String> suggestions) {
if (node.isEndOfWord()) {
suggestions.add(path.toString());
}
for (char ch = 'a'; ch <= 'z'; ch++) {
if (node.containsKey(ch)) {
path.append(ch);
collectClosestWords(node.get(ch), path, wordSet, suggestions);
path.deleteCharAt(path.length() - 1);
}
}
}
private void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
if (!node.containsKey(ch)) {
node.put(ch, new TrieNode());
}
node = node.get(ch);
}
node.setEndOfWord();
}
public static void main(String[] args) {
SpellChecker checker = new SpellChecker(new String[]{"apple", "bat", "cat", "fit", "fat"});
System.out.println("Is 'hat' a word? " + checker.isWord("hat"));
List<String> suggestions = checker.suggest("hat");
System.out.println("Suggestions for 'hat': " + suggestions);
}
}
这些题目和源码展示了字典树在解决实际问题中的应用。在面试中,能够根据问题的特点选择合适的算法并实现其解决方案是非常重要的。希望这些示例能够帮助你更好地准备面试!