数据结构——字典树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]
相关推荐
kida_yuan3 小时前
【Java】基于 Tabula 的 PDF 合并单元格内容提取
java·数据分析
SimonKing4 小时前
TeamViewer、向日葵平替?这几款免费远程控制软件,真香!
java·后端·程序员
Json____4 小时前
最近我用springBoot开发了一个二手交易管理系统,分享一下实现方式~
java·spring boot·后端
YuanlongWang4 小时前
C#基础——GC(垃圾回收)的工作流程与优化策略
java·jvm·c#
爱吃生蚝的于勒4 小时前
【Linux】深入理解进程(一)
java·linux·运维·服务器·数据结构·c++·蓝桥杯
毅炼4 小时前
常见排序算法
java·算法·排序算法
自由会客室4 小时前
在 Ubuntu24.04 上安装 JDK 21(Java 21)
java·开发语言
猫梦www4 小时前
力扣21:合并两个有序链表
数据结构·算法·leetcode·链表·golang·力扣
喜欢读源码的小白4 小时前
SpringBoot的启动流程原理——小白的魔法引擎探秘
java·开发语言·spring boot·springboot启动原理