字典树(前缀树)

简介: 字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。

字典树(前缀树)


概述:

字典树(Trie)用于判断字符串是否存在或者是否具有某种字符串前缀。
为什么需要用字典树解决这类问题呢?假如我们有一个储存了近万个单词的字典,即使我们使用哈希,在其中搜索一个单词的实际开销也是非常大的,且无法轻易支持搜索单词前缀。然而由于一个英文单词的长度 n 通常在 10 以内,如果我们使用字典树,则可以在 O(n)——近似 O(1)的时间内完成搜索,且额外开销非常小。



Trie(字典树),又称前缀树,是一棵有根树,其每个节点包含以下字段:

  • 指向子节点的指针数组 children。数组长度为 26,即小写英文字母的数量。此时children[0] 对应小写字母 a,children[1] 对应小写字母 b,…,children[25] 对应小写字母 z。
  • 布尔字段 isEnd,表示该节点是否为字符串的结尾。


插入字符串

我们从字典树的根开始,插入字符串。对于当前字符对应的子节点,有两种情况:

  • 子节点存在。沿着指针移动到子节点,继续处理下一个字符。
  • 子节点不存在。创建一个新的子节点,记录在 children 数组的对应位置上,然后沿着指针移动到子节点,继续搜索下一个字符。

重复以上步骤,直到处理字符串的最后一个字符,然后将当前节点标记为字符串的结尾。


查找前缀

我们从字典树的根开始,查找前缀。对于当前字符对应的子节点,有两种情况:

  • 子节点存在。沿着指针移动到子节点,继续搜索下一个字符。
  • 子节点不存在。说明字典树中不包含该前缀,返回空指针。

重复以上步骤,直到返回空指针或搜索完前缀的最后一个字符。

若搜索到了前缀的末尾,就说明字典树中存在该前缀。此外,若前缀末尾对应节点的 isEnd 为真,则说明字典树中存在该字符串。


java代码实现:

class Trie {
    TrieNode root;

    public Trie() {
        this.root = new TrieNode();
    }

    // 向字典树插入一个词
    void insert(String word) {
        TrieNode temp = root;
        for (int i = 0; i < word.length(); ++i) {
            if (temp.childNode[word.charAt(i) - 'a'] == null) {
                temp.childNode[word.charAt(i) - 'a'] = new TrieNode();
            }
            temp = temp.childNode[word.charAt(i) - 'a'];
        }
        temp.isVal = true;
    }

    // 判断字典树里是否有一个词
    boolean search(String word) {
        TrieNode temp = root;
        for (int i = 0; i < word.length(); ++i) {
            if (temp == null) {
                break;
            }
            temp = temp.childNode[word.charAt(i) - 'a'];
        }
        return temp != null ? temp.isVal : false;
    }

    // 判断字典树是否有一个以词开始的前缀
    boolean startsWith(String prefix) {
        TrieNode temp = root;
        for (int i = 0; i < prefix.length(); ++i) {
            if (temp == null) {
                break;
            }
            temp = temp.childNode[prefix.charAt(i) - 'a'];
        }
        return temp != null;
    }
}

class TrieNode {
    public TrieNode[] childNode;
    boolean isVal;

    public TrieNode() {
        childNode = new TrieNode[26];
        isVal = false;
        for (int i = 0; i < 26; ++i) {
            childNode[i] = null;
        }
    }
}


C++代码实现:

class TrieNode {
public:
    TrieNode* childNode[26];
    bool isVal;
    TrieNode(): isVal(false) {
        for (int i = 0; i < 26; ++i) {
            childNode[i] = nullptr;
        }
    }
};
class Trie {
    TrieNode* root;
public:
    Trie(): root(new TrieNode()) {}

    // 向字典树插入一个词
    void insert(string word) {
        TrieNode* temp = root;
        for (int i = 0; i < word.size(); ++i) {
            if (!temp->childNode[word[i]-’a’]) {
                temp->childNode[word[i]-’a’] = new TrieNode();
            }
            temp = temp->childNode[word[i]-’a’];
        }
        temp->isVal = true;
    }
    
    // 判断字典树里是否有一个词
    bool search(string word) {
    TrieNode* temp = root;
        for (int i = 0; i < word.size(); ++i) {
            if (!temp) {
                break;
            }
            temp = temp->childNode[word[i]-’a’];
        }
        return temp? temp->isVal: false;
    }
    
    // 判断字典树是否有一个以词开始的前缀
    bool startsWith(string prefix) {
        TrieNode* temp = root;
        for (int i = 0; i < prefix.size(); ++i) {
            if (!temp) {
                break;
            }
            temp = temp->childNode[prefix[i]-’a’];
        }
        return temp;
    }
};


练习:
208. Implement Trie (Prefix Tree)
目录
相关文章
|
编解码 自然语言处理 开发者
复刻Sora有多难?一张图带你读懂Sora的技术路径
OpenAI发布了视频生成模型Sora,最大的Sora模型能够生成一分钟的高保真视频。同时OpenAI称,可扩展的视频生成模型,是构建物理世界通用模拟器的一条可能的路径。
|
SQL 运维 关系型数据库
使用Binlog日志恢复误删的MySQL数据
今天文章的主题是如何使用Mysql内置的Binlog日志对误删的数据进行恢复,读完本文,你能够了解到: MySQL的binlog日志是什么?通常是用来干什么的? 模拟一次误删数据的操作,并且使用binlog日志恢复误删的数据。
1919 1
|
Java Nacos 微服务
JSR-330 ‘javax.inject.Inject‘ annotation found and supported for autowiring
这篇文章讨论了在Spring Boot项目中遇到的JSR-330 `javax.inject.Inject`注解相关问题,以及如何解决因版本不兼容导致服务注册失败的问题。
|
11月前
|
存储 算法 程序员
迪杰斯特拉(Dijkstra)算法(C/C++)
迪杰斯特拉(Dijkstra)算法(C/C++)
|
存储 SQL 关系型数据库
【MySQL进阶之路 | 基础篇】基本的SELECT语句及DESC显示表结构
【MySQL进阶之路 | 基础篇】基本的SELECT语句及DESC显示表结构
|
11月前
|
机器学习/深度学习 传感器 数据采集
使用Python实现深度学习模型:智能设备故障预测与维护
【10月更文挑战第10天】 使用Python实现深度学习模型:智能设备故障预测与维护
1541 2
|
C++ iOS开发 开发者
C++一分钟之-文件输入输出(I/O)操作
【6月更文挑战第24天】C++的文件I/O涉及`ifstream`, `ofstream`和`fstream`类,用于读写操作。常见问题包括未检查文件打开状态、忘记关闭文件、写入模式覆盖文件及字符编码不匹配。避免这些问题的方法有:检查`is_open()`、显式关闭文件或使用RAII、选择适当打开模式(如追加`ios::app`)以及处理字符编码。示例代码展示了读文件和追加写入文件的实践。理解这些要点能帮助编写更健壮的代码。
180 2
|
数据采集 Web App开发 JavaScript
Puppeteer实战案例:自动化抓取社交媒体上的媒体资源
Puppeteer实战案例:自动化抓取社交媒体上的媒体资源
|
负载均衡 监控 网络协议
TCP四次挥手:为什么四次?原理大揭密!
**TCP四次挥手详解**:客户端发送FIN进入FIN-WAIT-1,服务器回ACK进CLOSE-WAIT;服务器发送FIN,客户端回ACK进TIME-WAIT,等待2MSL确保数据传输完毕,防止新旧连接混淆。四次挥手确保双方完全关闭连接,解决数据丢失问题。过多TIME-WAIT可通过负载均衡、优化关闭顺序或调整系统参数缓解。关注“软件求生”获取更多技术内容!
268 0
|
Cloud Native NoSQL 数据管理
现代化数据管理:面向未来的数据库技术发展
传统数据库技术已经不能满足当今大数据时代的需求,现代化数据库技术的发展成为了当务之急。本文将探讨面向未来的数据库技术发展方向,包括云原生数据库、图数据库、区块链技术在数据库领域的应用以及数据库安全性等方面。