数据结构——字典树Trie(介绍、Java实现)

文章目录

字典树Trie

介绍

字典树是一种专门用于处理字符串的高效数据结构,也称为前缀树或单词查找树。

定义:

字典树是一种树形数据结构,用于存储字符串集合,具有以下特点:

  • 根节点不包含字符,每个子节点代表一个字符
  • 从根节点到某一节点的路径上经过的字符连接起来,构成对应的字符串
  • 每个节点的所有子节点代表的字符都不相同

核心特性:

  • 前缀共享:具有相同前缀的字符串共享路径
  • 快速检索:查找时间复杂度与字符串长度相关,与数据量无关
  • 前缀匹配:天然支持前缀搜索

Java实现

我们了解完字典树数据结构,尝试手搓一个简化版的字典树加强理解,在这个开源项目中实现。

GitCode - 全球开发者的开源社区,开源代码托管平台

  • 节点TrieNode
java 复制代码
public class TrieNode {
    private TrieNode[] children;    // 子节点数组,一共26个
    private boolean isWord; // 是否结尾即是否是单词结尾
    public TrieNode(){
        children = new TrieNode[26];
        isWord = false;
    }
    public boolean containKey(char c){
        return children[c - 'a'] != null;
    }
    public void putChar(char c){
        children[c - 'a'] = new TrieNode();
    }
    public TrieNode getChild(char c){
        if (children[c - 'a'] == null) return null;
        return children[c - 'a'];
    }
    public void setIsWord(boolean isWord){
        this.isWord = isWord;
    }
    public boolean isWordOrNot(){
        return isWord;
    }
}
  • 字典树Trie
java 复制代码
public class Trie {
    private TrieNode root;
    /**
     * 构造函数,初始化Trie树
     * 创建一个空的根节点
     */
    public Trie() {
        root = new TrieNode();
    }

    /**
     * 插入单词到Trie树中
     * @param word 要插入的单词
     */
    public void insert(String word) {
        if(word == null || word.length() == 0) return;
        TrieNode node = root;
        // 遍历单词字母
        for(int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!node.containKey(c)){
                node.putChar(c);
            }
            node = node.getChild(c);
        }
        node.setIsWord(true); // 设置为单词结尾
    }

    /**
     * 在Trie树中搜索指定单词
     * @param word 要搜索的单词
     * @return 如果单词存在返回true,否则返回false
     */
    public boolean searchWord(String word) {
        if(word == null || word.length() == 0) return false;
        TrieNode node = searchPrefix(word);
        return node != null && node.isWordOrNot();
    }

    /**
     * 检查Trie树中是否存在指定前缀
     * @param prefix 要检查的前缀
     * @return 如果前缀存在返回true,否则返回false
     */
    public boolean startsWith(String prefix) {
        if(prefix == null || prefix.length() == 0) return false;
        return searchPrefix(prefix) != null;
    }

    /**
     * 查找前缀对应的节点
     * @param prefix 要查找的前缀
     * @return 如果前缀存在,返回前缀最后一个字符对应的节点;否则返回null
     */
    private TrieNode searchPrefix(String prefix) {
        TrieNode node = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (!node.containKey(c)){
                return null;
            }
            node = node.getChild(c);
        }
        return node;
    }

    /**
     * 搜索所有以指定前缀开头的单词
     * @param prefix 搜索的前缀
     * @return 所有匹配的单词列表
     */
    public List<String> searchWordsWithPrefix(String prefix){
        if(prefix == null || prefix.length() == 0) return new ArrayList<>();
        List<String> result = new ArrayList<>();
        TrieNode node = searchPrefix(prefix);
        if(node != null){
            collectWordsStartWithPrefix(node, result, new StringBuilder(prefix));
        }
        return result;
    }

    /**
     * 递归收集以指定节点开始的所有单词
     * @param node 当前节点
     * @param result 结果列表,用于存储找到的单词
     * @param strbuilder 当前构建的字符串
     */
    private void collectWordsStartWithPrefix(TrieNode node, List<String> result, StringBuilder strbuilder) {
        if(node.isWordOrNot()){
            result.add(strbuilder.toString());
        }
        for(int i = 0;i < 26;i ++){
            char c = (char)('a' + i);
            if(node.containKey(c)){
                strbuilder.append(c);
                collectWordsStartWithPrefix(node.getChild(c), result, strbuilder);
                strbuilder.deleteCharAt(strbuilder.length() - 1); // 回溯
            }
        }
    }

    public static void main(String[] args) {
        Trie trie = new Trie();
        trie.insert("apple");
        trie.insert("application");
        trie.insert("blue");
        trie.insert("blueface");
        trie.insert("blwww");
        System.out.println(trie.searchWord("blue"));
        System.out.println(trie.searchWordsWithPrefix("app"));
        System.out.println(trie.searchWordsWithPrefix("bl"));
    }
}
  • 测试结果:
plain 复制代码
true
[apple, application]
[blue, blueface, blwww]
相关推荐
WX-bisheyuange1 小时前
基于Spring Boot的教师个人成果管理系统的设计与实现
java·spring boot·后端
xunyan62342 小时前
面向对象(上)-封装性的引入
java·开发语言
脸大是真的好~3 小时前
黑马JAVAWeb-05 JDBC入门-预编译SQL-Mybatis入门-Mybatis日志输出-数据库连接池-增删改查-XML映射配置
java
还算善良_3 小时前
XML签名
xml·java·开发语言
梅梅绵绵冰3 小时前
xml方式实现AOP
xml·java·开发语言
桦说编程4 小时前
Guava 迭代器增强类介绍
java·后端·设计模式
王璐WL4 小时前
【数据结构】双向链表
数据结构
谢景行^顾4 小时前
数据结构知识掌握
linux·数据结构·算法
235164 小时前
【JVM】Java为啥能跨平台?JDK/JRE/JVM的关系?
java·开发语言·jvm·spring boot·后端·spring·职场和发展
courtfu4 小时前
Plugin ‘mysql_native_password‘ is not loaded`
java·后端