• 力扣208——实现 Trie (前缀树)


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

    原题

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

    示例:

    Trie trie = new Trie();
    
    trie.insert("apple");
    trie.search("apple");   // 返回 true
    trie.search("app");     // 返回 false
    trie.startsWith("app"); // 返回 true
    trie.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. 布尔字段,以指定节点是对应键的结尾还是只是键前缀。

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

    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/

    公众号:健程之道

  • 相关阅读:
    虚拟机网络不通
    设计师升职加薪必须知道的10个设计网站
    Centos设置防火墙与开放访问端口
    设置Jexus开机启动
    .Net 操作Excel表格
    Core 2.0使用Nlog记录日志+Mysql
    C# 操作docx文档
    JS截取页面,并保存到本地
    XmlReader 使用
    requireJS简单应用
  • 原文地址:https://www.cnblogs.com/death00/p/12164983.html
Copyright © 2020-2023  润新知