构造识别所有活前缀的dfa_刷leetcode——实现 Trie (前缀树)

本文详细介绍了如何利用前缀树(Trie)解决LeetCode中的问题,探讨了前缀树在字符串搜索中的优势,并提供了构造前缀树节点的代码示例。通过解题过程,阐述了前缀树在存储和查找效率上的优越性,以及其在空间复杂度上的优化。最后,鼓励读者深入理解前缀树的概念并提供了更多学习资源。
摘要由CSDN通过智能技术生成

这道题主要是构造前缀树节点的数据结构,帮助解答问题。

原题

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

示例:

Trie trie = new Trie();trie.insert("apple");trie.search("apple");   // 返回 truetrie.search("app");     // 返回 falsetrie.startsWith("app"); // 返回 truetrie.insert("app");   trie.search("app");     // 返回 true

说明:

  • 你可以假设所有的输入都是由小写字母 a-z 构成的。
  • 保证所有输入均为非空字符串。

原题url:https://leetcode-cn.com/problems/implement-trie-prefix-tree/

解题

前缀树的意义

我们用前缀树这种数据结构,主要是用在在字符串数据集中搜索单词的场景,但针对这种场景,我们也可以使用平衡树和哈希表,而且哈希表可以在O(1)时间内寻找到键值。那为什么还要前缀树呢?

原因有3:

  1. 前缀树可以找到具有同意前缀的全部键值。
  2. 前缀树可以按词典枚举字符串的数据集。
  3. 前缀树在存储多个具有相同前缀的键时可以使用较少的空间,只需要O(m)的时间复杂度,其中 m 为键长。在平衡树中查找键值却需要O(m log n),其中 n 是插入的键的数量;而哈希表随着大小的增加,会出现大量的冲突,时间复杂度可能增加到O(n)。

构造前缀树的节点结构

既然是树,肯定也是有根节点的。至于其节点结构,需要有以下特点:

  1. 最多 R 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。本题中假定 R 为 26,小写拉丁字母的数量。
  2. 布尔字段,以指定节点是对应键的结尾还是只是键前缀。
b7ad209aa0320523147e3f5eccc87a26.png

接下来让我们看看节点结构的代码:

class TrieNode {    TrieNode[] nodes;    boolean isEnd;    public TrieNode() {        // 26个小写英文字母        nodes = new TrieNode[26];        // 当前是否已经结束        isEnd = false;    }                /**         * 当前节点是否包含字符 ch         */    public boolean contains(char ch) {        return nodes[ch - 'a'] != null;    }                /**         * 设置新的下一个节点         */    public TrieNode setNode(char ch, TrieNode node) {            // 判断当前新的节点是否已经存在        TrieNode tempNode = nodes[ch - 'a'];                // 如果存在,就直接返回已经存在的节点        if (tempNode != null) {            return tempNode;        }        // 否则就设置为新的节点,并返回        nodes[ch - 'a'] = node;        return node;    }                /**         * 获取 ch 字符         */    public TrieNode getNode(char ch) {        return nodes[ch - 'a'];    }                /**         * 设置当前节点为结束         */    public void setIsEnd() {        isEnd = true;    }    /**         * 当前节点是否已经结束         */    public boolean isEnd() {        return isEnd;    }}

接下来就是真正的前缀树的结构:

class Trie {    /**         * 根节点         */    TrieNode root;    /** Initialize your data structure here. */    public Trie() {        root = new TrieNode();    }        /** Inserts a word into the trie. */    public void insert(String word) {        TrieNode before = root;        TrieNode node;                // 遍历插入单词中的每一个字母        for (int i = 0; i < word.length(); i++) {            node = new TrieNode();            node = before.setNode(word.charAt(i), node);            before = node;        }                // 设置当前为终点        before.setIsEnd();    }        /** Returns if the word is in the trie. */    public boolean search(String word) {        TrieNode before = root;        TrieNode temp;                // 遍历查找        for (int i = 0; i < word.length(); i++) {            temp = before.getNode(word.charAt(i));            if (temp == null) {                return false;            }            before = temp;        }                // 且最后一个节点也是终点        return before.isEnd();    }        /** Returns if there is any word in the trie that starts with the given prefix. */    public boolean startsWith(String prefix) {        TrieNode before = root;        TrieNode temp;                // 遍历查找        for (int i = 0; i < prefix.length(); i++) {            temp = before.getNode(prefix.charAt(i));            if (temp == null) {                return false;            }            before = temp;        }        return true;    }}

提交OK,执行用时:43 ms,内存消耗:55.3 MB,虽然只战胜了87.40%的提交,但试了一下最快的那个代码,和我这个方法在时间上基本没什么差别,应该是当初提交的时候测试用例没有那么多吧。

总结

以上就是这道题目我的解答过程了,不知道大家是否理解了。这道题目可能需要专门去理解一下前缀树的用途,这样可以有助于构造前缀树的结构。

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

https://death00.github.io/

公众号:健程之道

a8aee3b998f26be9a89edf9c9a209be7.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值