数据结构——trie树介绍

栏目: 数据库 · 发布时间: 5年前

内容简介:1.本文原文来自于leetcode上的算法题Implement Trie的解决方案.2.原文地址3.新手献丑了,希望大家轻拍~(微笑)

1.本文原文来自于leetcode上的算法题Implement Trie的解决方案.

2.原文地址

3.新手献丑了,希望大家轻拍~(微笑)

二、原文在这

1.问题描述

算法题:

通过编写插入、查询、判断开头等方法完成一个trie树。

例子:

Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // returns true
trie.search("app");     // returns false
trie.startsWith("app"); // returns true
trie.insert("app");   
复制代码

提示:

  • 你可以假设所有的输入都是由小写字母a-z组成的。
  • 所有的输入string数组都不为空。

总结:

这篇文章是写给中等水平的读者的,将会介绍数据结构trie(前缀树)和其中的常见操作。

2.解决方法:

2.1应用:

trie(前缀树)是一种树形数据结构,常常用来在字符串的数据集中检索一个关键词。目前,trie数据结构已经被高效地应用在了很多领域:

(1)自动填充

数据结构——trie树介绍
图片1.谷歌实时的关键词推荐

(2)拼写检查

数据结构——trie树介绍
图片2.在文字处理机中的拼写检查

(3)IP路由(最长的路由匹配)

数据结构——trie树介绍
图片3.最长路由匹配算法

(4)九键输入法的预测文字

数据结构——trie树介绍
图片4.九键输入法在1990年代应用在手机中用来输入文字

(5)完成文字游戏

数据结构——trie树介绍
图片5.通过减少搜索空间,trie能够很好的完成boggle这个游戏
有很多其他的数据结构如,平衡树,hash表都能够在一个string的数据集中查找单词,但是我们为什么要使用trie呢?虽然hash表对于找到一个关键词(key)只需要 O

(1)的时间复杂度,但是在下列操作中,它就表现得不是很高效了。

  • 找到拥有共同前缀的所有关键词(key)。
  • 根据字典序枚举所有字符串

trie优于hash表的另外一个原因是,但hash表的数据规模扩大后,将会出现很多的hash碰撞,因此查询时间复杂度将会提高到 O (n), n 是插入的关键词的个数。而相比于hash表,trie在存储很多具有共同前缀的关键词时需要的空间更少。在这个例子里trie只需要 O (m)的时间复杂度( m 是关键词的长度)。而在平衡树里查询一个关键词,则需要花费 O ( m log n )

2.2 trie节点结构

trie是一个有根树,它的节点有以下几个字段:

  • 与子节点间最多有R个连接,每个连接对应到R个字母中的一个。这个R个字母来自于字母表。在这篇文章中,我们假设R是26,即26个小写的拉丁字母。
  • 一个布尔值isEnd,说明该布尔值说明当前节点是否是一个关键词的结尾,否则就只是该关键词的前缀。
    数据结构——trie树介绍
    图6.代表关键字“leet”在trie中的表达
    java编写的trie节点
class TrieNode {

    // R links to node children
    private TrieNode[] links;

    private final int R = 26;

    private boolean isEnd;

    public TrieNode() {
        links = new TrieNode[R];
    }

    public boolean containsKey(char ch) {
        return links[ch -'a'] != null;
    }
    public TrieNode get(char ch) {
        return links[ch -'a'];
    }
    public void put(char ch, TrieNode node) {
        links[ch -'a'] = node;
    }
    public void setEnd() {
        isEnd = true;
    }
    public boolean isEnd() {
        return isEnd;
    }
}
复制代码

2.3 trie中最常见的操作——添加和查询关键词

(1)添加关键词到trie中

我们通过遍历trie来插入关键词。我们从根节点开始,搜寻和关键词第一个字母对应的连接,这里一般有两种情况:

  • 如果连接存在,那么我们就顺着这个连接往下移到下一子层,接着搜寻关键词的下一个字母对应的连接。
  • 如果连接不存在,那么我们就新建一个trie节点,对应着现在的关键词字母,建立与父节点的连接。

我们重复这个步骤,直到处理完关键词的最后一个字母,然后标记最后的节点为结束节点。算法结束。

数据结构——trie树介绍
插入关键字到trie中

java编写的插入方法

class Trie {
    private TrieNode root;

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

    // Inserts a word into the trie.
    public void insert(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
            char currentChar = word.charAt(i);
            if (!node.containsKey(currentChar)) {
                node.put(currentChar, new TrieNode());
            }
            node = node.get(currentChar);
        }
        node.setEnd();
    }
}
复制代码

复杂度分析:

  • 时间复杂度 O (m), m 是关键词的长度。在算法的每一次循环中,我们要么检查节点要么新建一个节点,直到该关键词的最后一个字母。所以,这只需要进行m次操作。
  • 空间复杂度O(m)。最糟糕的情况是,新插入的关键词和已经存在trie的关键词没有共同的前缀,因此我们必须插入m个新的节点,因此需要O(m)空间复杂度。

(2)在trie中搜索关键词

每一个关键词在trie中都可以被一条从根节点到子节点的路径所表示。我们将根据关键词的第一个字母从根节点开始搜索,然后检查节点上的每一个连接对应的字母,一般有两种情况:

  • 存在对应关键词字母的连接,我们将从该连接移动到下一个节点,然后搜索关键词的下一个字母对应的连接。
  • 对应的连接不存在,如果此时已经遍历到了关键词的最后一个字母,则把当前的节点标记为结束节点,然后返回true。当然还有另外两种情况,我们会返回false:
    • 关键词的字母没有遍历完,但没有办法接着在trie中找到根据关键词字母的形成的路径,所以trie中不存在该关键词。
    • 关键词的字母的已经遍历完了,但当前的节点不是结束节点,因此搜索的关键词只是trie中的某一个关键字的前缀。
数据结构——trie树介绍
图8.在trie中搜索某个关键词

java编写搜索关键词的方法

class Trie {
    ...

    // search a prefix or whole key in trie and
    // returns the node where search ends
    private TrieNode searchPrefix(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
           char curLetter = word.charAt(i);
           if (node.containsKey(curLetter)) {
               node = node.get(curLetter);
           } else {
               return null;
           }
        }
        return node;
    }

    // Returns if the word is in the trie.
    public boolean search(String word) {
       TrieNode node = searchPrefix(word);
       return node != null && node.isEnd();
    }
}
复制代码

复杂度分析:

  • 时间复杂度:O(m)。在算法的每一步都是搜索关键词的下一个字母,因此在最差的情况下,算法需要执行m步。
  • 空间复杂度:O(1)。

(3)在trie中搜索关键词的前缀

这个方法和我们在trie中用来搜索关键词的方法很类似。我们从根节点开始移动,直到关键词前缀的每个字母都被搜索到了,或者,没有办法在trie中根据关键词的当前字母找到接下去的路径。这个方法和前面提到的搜索关键词的唯一不同在于,当我们遍历到关键词前缀的最后一个字母时,我们总是返回true,我们不需要考虑当前的节点是否有结束标志,因为我们只是搜索关键词的前缀,而不是整个关键词。

数据结构——trie树介绍
图9.在trie中搜索关键词前缀

java编写的搜索关键词前缀的方法

class Trie {
    ...

    // Returns if there is any word in the trie
    // that starts with the given prefix.
    public boolean startsWith(String prefix) {
        TrieNode node = searchPrefix(prefix);
        return node != null;
    }
}
复制代码

复杂度分析:

  • 时间复杂度:O(m)
  • 空间复杂度:O(1)

3. 应用问题

这里有一些非常合适应大家去练习的问题,这些问题都能用trie数据结构解决。

这篇分析出自@elmirap


以上所述就是小编给大家介绍的《数据结构——trie树介绍》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Effective JavaScript

Effective JavaScript

David Herman / Addison-Wesley Professional / 2012-12-6 / USD 39.99

"It's uncommon to have a programming language wonk who can speak in such comfortable and friendly language as David does. His walk through the syntax and semantics of JavaScript is both charming and h......一起来看看 《Effective JavaScript》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具