788-搜索引擎和trie字典树

大家经常使用Microsoft或者google浏览器, 当我们输入搜索内容后,有自动提示的功能,节省了时间。那么,这个自动提示功能是怎样实现的呢?
在这里插入图片描述
关于搜索的匹配,可以用哈希表,也可以用红黑树,其实,今天要说的trie字典树更适合这种场景,因为trie树能实现前缀匹配,具有天然的优势。

trie树

trie树,就是retrieval树,也叫检索树,字典树,名如其实,我们先来看看trie树的数据结构
以"cat", “dog”, “cattle”, "bag"为例,trie树结构如下:
在这里插入图片描述
可以看到,根结点不存储字母,其它每个结点,存储的是字母
如果用户输入"cat", 就能帮助用户联想到"cat"和"cattle", 实现了提示功能。

trie树的实现

很显然,对于每一个节点而言,它要存储一个字母(根结点除外),而且还可能有26个孩子结点,所以需要26个指针。

除此之外,在节点中,还需要标注该结点是否为终点节点,如使用isComplete标识,所以,每个结点的数据结构是:

//trie结点
class TrieNode
{
public:
    char ch;//存储字母
    TrieNode* child[26];//指向26个可能的孩子结点
    bool isComplete;//该结点是否为终结结点

    TrieNode()//构造函数
    {
        for (int i = 0; i < 26; i++)
        {
            child[i] = NULL;
            isComplete = false;
        }
    }
};

trie树的常规操作是:
插入一个单词,即insert方法

查找一个单词,即search方法

查找一个前缀,即startWith方法

整个程序如下:

#include <iostream>

using namespace std;

//trie结点
class TrieNode
{
public:
    char ch;//存储字母
    TrieNode* child[26];//指向26个可能的孩子结点
    bool isComplete;//该结点是否为终结结点

    TrieNode()//构造函数
    {
        for (int i = 0; i < 26; i++)
        {
            child[i] = NULL;
            isComplete = false;
        }
    }
};

//trie树, 为简便起见,不考虑结点堆内存的释放
class Trie
{
private:
    TrieNode* t;

public:
    Trie()
    {
        t = new TrieNode();
    }

    //插入一个单词
    void insert(const string& str)
    {
        int len = str.size();
        if (len > 0)
        {
            TrieNode* p = t;
            for (int i = 0; i < len; i++)
            {
                int j = str[i] - 'a';
                if (p->child[j] == NULL)
                {
                    p->child[j] = new TrieNode();
                    p->child[j]->ch = str[i];
                }

                p = p->child[j];
            }

            p->isComplete = true;
        }
    }

    //查找一个单词
    bool search(const string& str)
    {
        int len = str.size();
        TrieNode* p = t;
        for (int i = 0; i < len; i++)
        {
            int j = str[i] - 'a';
            if (p->child[j] == NULL)
            {
                return false;
            }
            p = p->child[j];
        }

        if (p->isComplete)
        {
            return true;
        }

        return false;
    }

    //查找前缀
    bool startsWith(const string& str)
    {
        int len = str.size();
        if (len <= 0)
        {
            return false;
        }

        TrieNode* p = t;
        for (int i = 0; i < len; i++)
        {
            int j = str[i] - 'a';
            if (p->child[j] == NULL)
            {
                return false;
            }
            p = p->child[j];
        }

        return true;
    }
};

int main()
{
    Trie* t = new(Trie);
    t->insert("cat");
    t->insert("dog");
    t->insert("cattle");
    t->insert("bag");

    cout << t->search("cat") << endl;
    cout << t->search("bag") << endl;
    cout << t->startsWith("ca") << endl;

    cout << t->search("catt") << endl;
    cout << t->search("pig") << endl;
    cout << t->search("") << endl;
    cout << t->startsWith("") << endl;
    cout << t->startsWith("cab") << endl;
}

在这里插入图片描述
从上面的程序中,很容易分析出trie树的时间复杂度。而且,不可否认,trie树是比较耗费空间的,因为要存储很多指针信息。

但是trie树在前缀查找方面有独特的优势,速度非常快。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值