Trie 树——搜索关键词提示

时间:2022-03-18 20:40:29

当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构。

1. 什么是 “Trie” 树

Trie 树也叫 “字典树”。顾名思义,它是一个树形结构,专门用来处理在一组字符串集合中快速查找某个字符串的问题。

假设我们有 6 个字符串,它们分别是:how,hi,her,hello,so,see。我们希望在这里面多次查找某个字符串是否存在,如果每次都拿要查找的字符串和这六个字符串依次进行匹配,那效率就会比较低。

如果我们可以对这六个字符串做一下预处理,组织成 Trie 树的结构,那之后每次查找,都只要在 Trie 树中进行匹配即可。Trie 树的本质,就是利用字符串之间的公共前缀,将重复的前缀合并在一起

Trie 树——搜索关键词提示

其中,根节点不包含任何信息,每个节点代表字符串中的一个字符,从根节点到红色节点的一条路径表示一个字符串。注意红色节点并不都是叶子节点,比如有两个词 how 和 however,那么 w 和 r 都是红色节点。一个 Trie 树的构造过程如下所示。

Trie 树——搜索关键词提示
Trie 树——搜索关键词提示

当我们要在构建好的 Trie 树中查找一个字符串的时候,那就要将查找的字符串分割成单个的字符,然后从根节点开始匹配。如下面的例子所示,绿色路径就是 “her” 的匹配路径,而 “he” 的最后一个匹配节点并不是红色节点,所以其并不能完全匹配任何字符串。

Trie 树——搜索关键词提示
Trie 树——搜索关键词提示

2. 如何实现一棵 Trie 树

从上面我们可以看到,Trie 树主要有两个操作:一个是将字符串集合构建成 Trie 树,另一个是在 Trie 树中查询一个字符串

Trie 树是一个多叉树结构,其子节点个数事先未知,但我们可以借助散列表的思想,在下标与字符之间建立一个一一映射,来存储子节点的指针。

Trie 树——搜索关键词提示

假设我们的字符串只有 a 到 z 这 26 个字母,那么数组下标为 0 的元素就存储指向子节点 a 的指针,下标为 1 的元素就存储指向子节点 b 的指针,以此类推,下标为 25 的元素就存储指向子节点 z 的指针。如果某个字符的子节点不存在,那对应该下标位置的元素就为 NULL。当我们在 Trie 树中进行查找的时候,就可以拿字符的 ASCII 码减去 'a' 的 ASCII 码来获取其子节点的指针。

#include <iostream>
#include <cstring>

using namespace std;

class TrieNode
{
public:

    char data;
    bool is_ending_char;
    TrieNode *children[26];

    TrieNode(char ch)
    {
        data = ch;
        is_ending_char = false;
        for (int i = 0; i < 26; i++)
            children[i] = NULL;
    }
};

class Trie
{
private:

    TrieNode *root;

public:

    // 构造函数,根节点存储无意义字符 '/'
    Trie()
    {
        root = new TrieNode('/');
    }

    // 向 Trie 树中添加一个字符串
    void insert_string(const char str[])
    {
        TrieNode *cur = root;
        for (unsigned int i = 0; i < strlen(str); i++)
        {
            int index = int(str[i] - 'a');
            if (cur->children[index] == NULL)
            {
                TrieNode *temp = new TrieNode(str[i]);
                cur->children[index] = temp;
            }
            cur = cur->children[index];
        }
        cur->is_ending_char = true;
    }

    // 在 Trie 树中查找一个字符串
    bool search_string(const char str[])
    {
        TrieNode *cur = root;
        for (unsigned int i = 0; i < strlen(str); i++)
        {
            int index = int(str[i] - 'a');
            if (cur->children[index] == NULL)
            {
                return false;
            }
            cur = cur->children[index];
        }
        if (cur->is_ending_char == true) return true;
        else return false;
    }
};

int main()
{
    char str[][8] = {"how", "hi", "her", "hello", "so", "see", "however"};

    Trie test;
    for (int i = 0; i < 7; i++)
    {
        test.insert_string(str[i]);
    }

    cout << "Finding \'her\': " << test.search_string("her") << endl;
    cout << "Finding \'he\': " << test.search_string("he") << endl;
    cout << "Finding \'how\': " << test.search_string("how") << endl;
    cout << "Finding \'however\': " << test.search_string("however") << endl;

    return 0;
}

在构建 Trie 树的过程中,需要扫描所有的字符串,时间复杂度为 O(n),其中 n 表示所有字符串的长度之和。而在 Trie 树中进行查找的话,如果待查找字符串的长度为 k 的话,那最多只需要对比 k 个节点即可,时间复杂度为 O(k)。

3. Trie 树的内存消耗

在上面的例子中,Trie 树的每个节点都要存储 26 个指针,尽管某些节点的子节点很少,我们依然要维护这么一个长度的数组。另外,如果字符串中不仅包含小写字母,而且包含大写字母、数字甚至是中文等,那就会需要更多的存储空间。也就是说,在某些情况下,Trie 树并不一定会节省内存空间,尤其是在重复前缀不多的时候。

当然,尽管 Trie 树可能会很浪费内存,但是确实非常高效,这也是一种空间换时间的折中。如果我们可以稍微牺牲一点查询的效率,那就可以选用数组、散列表、红黑树等其他数据结构来存储一个节点的子节点指针。

假设我们使用数组,数组中的指针按照所指向子节点的字符大小顺序排列。这样,在查找的时候,我们可以通过二分算法来快速找到指向子节点的指针。但是,在往 Trie 树中插入字符串的话,为了维护数组的有序性,就会稍微慢了点。

另外,还可以采用缩点优化,将只有一个子节点而且不是结束节点的节点与其子节点进行合并,来节省空间,但这也增加了编码难度。

Trie 树——搜索关键词提示

4. Trie 树与散列表、红黑树的比较

在字符串匹配或者说查找问题上,Trie 树对要处理的字符串有极其严格的要求。

  • 字符串中包含的字符集不能太大;

  • 字符串的前缀重合比较多;

  • 从零开始实现一个 Trie 树,比较复杂,不便于维护;

  • Trie 树中利用指针来存储数据,不利用利用缓存。

因此,在工程中,我们更倾向于使用散列表或者红黑树,它们都不需要自己去实现,直接利用编程语言中提供的线程类库就行。实际上,Trie 树不适合这种精确查找,更适合的是查找前缀匹配的字符串,也就是搜索时的关键词提示功能。

5. 搜索关键词提示功能的实现

假设关键词库由用户的热门搜索关键词组成,我们将这个词库构建成一个 Trie 树。当用户输入其中某个单词的时候,把这个词作为一个前缀子串在 Trie 树中匹配。还以上面为例,当用户输入 'h' 时,我们就可以将以 'h' 为前缀的单词 hello,her,hi,how 展示在搜索提示框,当用户输入 'he' 时,我们就可以将以 'h' 为前缀的单词 hello,her 展示在搜索提示框。这就是搜索关键词提示的最基本的算法原理。

Trie 树——搜索关键词提示

另外,Trie 树还可以扩展到更加广泛的应用上,比如输入法、代码编辑器和浏览器的自动输入补全功能。

参考资料-极客时间专栏《数据结构与算法之美》

获取更多精彩,请关注「seniusen」!
Trie 树——搜索关键词提示

Trie 树——搜索关键词提示的更多相关文章

  1. 用trie树实现输入提示功能,输入php函数名,提示php函数

    参照刘汝佳的trie树 结构体 #include "stdio.h" #include "stdlib.h" #include "string.h&q ...

  2. Trie&vert;如何用字典树实现搜索引擎的关键词提示功能

    Trie字典树 Trie字典树又称前缀树,顾名思义,是查询前缀匹配的一种树形数据结构 可以分为插入(创建) 和 查询两部分.参考地址极客时间 下图为插入字符串的过程: 创建完成后,每个字符串最后一个字 ...

  3. Trie树简介

    Trie树, 即字典树, 又称单词查找树或键树, 多叉树 基本性质 根节点不包含字符,除根节点外每一个节点都只包含一个字符 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串 每个节点 ...

  4. 【转】B树、B-树、B&plus;树、B&ast;树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

    B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: ...

  5. Trie树(字典树) 最热门的前N个搜索关键词

    方法介绍 1.1.什么是Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优 ...

  6. 搜索关键词智能提示suggestion

    转载自:stormbjm的专栏 题目详情:百度搜索框中,输入“北京”,搜索框下面会以北京为前缀,展示“北京爱情故事”.“北京公交”.“北京医院”等等搜索词,输入“结构之”,会提示“结构之法”,“结构之 ...

  7. cogs 647&period; &lbrack;Youdao2010&rsqb; 有道搜索框 Trie树 字典树

    647. [Youdao2010] 有道搜索框 ★☆   输入文件:youdao.in   输出文件:youdao.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 在有 ...

  8. Trie树

    一.什么是trie树 1.Trie树 (特例结构树)   Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串( ...

  9. 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion&comma;附近点搜索

    第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...

随机推荐

  1. java 关键字 assert的学习

    之前在学习java源码时,发现了assert这个不常用的关键字.下面直接来介绍下这个关键字的使用. assert是什么? 它是jdk1.4之后新增加的关键字,没了. assert的作用是什么? ass ...

  2. 《Pro AngularJS》学习小结-02

    上一篇的项目只有一个单独的模板页面,加入了相应的controller,filter,使得页面上的数据能够动态的变化.现在我们开始建立并整合多个模板,加入购物车模块和结账checkout模块. 一.在页 ...

  3. 问题: 数据流中位数 求解 时间复杂度度 java

    今天练习了一题: 数据流中位数 问题描述:数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数. 案例: 持续进入数组的数的列表为:[1, 2, 3, 4, 5],则返回[1 ...

  4. Jmeter发送邮件功能SMTP Sampler

    介绍Jmeter的发送邮件功能,使用的Sampler是SMTP Sampler,详细说明每个配置项的功能 从上往下介绍需要用到的配置项: Server settings Server: 服务器地址 P ...

  5. sql server 性能调优之 资源等待 CXPACKET

    一.概述  CXPACKET是指:线程正在等待彼此完成并行处理.什么意思呢? 当sql server发现一条指令复杂时,会决定用多个线程并行来执行,由于某些并行线程已完成工作,在等待其它并行线程来同步 ...

  6. 【TP5&period;0】model的操作方法

    tp5 中 model 的新增方法 //默认主键为自动识别,如果需要指定,可以设置属性: namespace app\index\model; use think\Model; class User ...

  7. Django入门&lpar;二&rpar;

    这一节主要介绍django中的model,template模板. model是django自带的orm框架,下面我们来搭建一个博客网站,来看看是如何使用的. 1.新建应用blog python man ...

  8. AutoCompleteTextView

    在本节中作者只写了AutoCompleteTextView 和MutiAutoCompleteTextView 的用法,没有写怎样得到选中的值,我做了如下修改,增加按钮获取值赋值给TextView p ...

  9. 源码编译安装mysql-boost-5&period;7&period;16&period;tar&period;gz报错分析处理

    Plugin 'FEDERATED' is disabled.  mysqld: Table 'mysql.plugin' doesn't exist  [ERROR] Can't open the ...

  10. Spring Cloud组件完整

    有关项目启动和配置的说明: 1.最先启动的是eureka-server,并且你需要在整个测试过程中保持它的启动状态,因为它是注册中心,大多数服务必须依赖于它才能实现必要的功能. 2.如果你想测试配置中 ...