数据结构——字典树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]
相关推荐
葡萄成熟时 !6 小时前
JAVA 常用API学习笔记
java·笔记·学习
Rain的Java大神之路7 小时前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
石头猫灯8 小时前
WordPress wp2shell 漏洞链完整拆解流程
网络·数据结构·安全·web安全
吴声子夜歌8 小时前
Java面试题——基础(一)
java·开发语言
南城以南溫暖如初1479 小时前
外卖CPS实战指南:从技术选型到运营落地全流程解析
java·spring boot·mysql·架构·vue
傻啦嘿哟9 小时前
英雄联盟皮肤爬虫:爬取全皮肤价格与特效,做比价工具
java·c++·爬虫
码匠许师傅10 小时前
【C++ 面试真题】24. 聊聊 C++ 的时间日期处理
java·c++·面试
mqiqe11 小时前
AgentScope Java 2.0 Agent 状态存储(AgentStateStore)深度解析:构建可恢复、可扩展的智能体运行时
java·运维·网络
zhifou12345611 小时前
java 17升级安装
java·开发语言
用户37215742613511 小时前
如何使用 Java 将 Markdown 转换为 PDF(含自定义设置)
java