数据结构——字典树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]
相关推荐
弹简特13 分钟前
【Java项目-企悦抽】15-活动管理模块02-活动创建测试与活动列表实现
java·开发语言·springboot
斯内普吖14 分钟前
(开源)水果蔬菜商城实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源
wang_shu_mo_ran21 分钟前
Spring IoC和DI概念篇
java·后端·spring
小狼1545443 分钟前
拼多多订单数据导出 CSV 实战:接口分页、字段平铺与 5 个数据坑,多多开票助手
java·前端·javascript
Joe_Wang51 小时前
【从0到1学习JVM · 25】同样都要停顿所有线程,Parallel比Serial到底强在哪
java·jvm·学习·垃圾回收
wuminyu1 小时前
JVM虚拟线程的底层实现原理分析
java·linux·c语言·jvm·c++
keyipatience1 小时前
跳表详细解析
开发语言·数据结构·c++·算法·跳表
小小、码农1 小时前
〖Linux文件系统〗:彻底打通文件 IO 全链路
linux·开发语言·数据结构·c++·系统
弹简特1 小时前
【Java项目-企悦抽】13-奖品管理模块-奖品列表和创建奖品的实现
java·开发语言·网络·springboot
小蒜学长1 小时前
基于Spring Boot+Vue的“禾源”农产品销售系统设计与实现(代码+数据库+LW)
java·spring boot·后端·农产品销售系统·产品溯源