走入字典树(一)


前言

字典树作为一种十分重要的数据结构,在大学的数据结构课堂上,抑或是算法课堂上都没有介绍。而同时又有许多小伙伴想要快速简单的了解字典树。那么这一系列文章将会通过文字+实战的方式教会你如何去学习字典树,话不多说,我们进入正题。
`

一、什么是字典树

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。[摘录自百度百科]

简而言之,字典树的功能可以让我们以下几点:
(1) 判断一个字符串是否在已有的字符串中出现

(2) 判断一个字符串是否是已有的字符串的前缀

二、图形化字典树

字典树小例子
在这个字典树中记录了三个字符串,分别是aac 、 b 、 za。我们所需要做的就是构建出这么一棵字典树,然后做一些例如查询a是否是其中一个字符串的前缀,是否存在字符串za之类的工作即可。

二、字典树代码模板

1、分块介绍字典树模板

(该模板只包含字符串中只有26个字母,其他情况另当别论,不过学会这个可以举一反三,触类旁通)

1.1 定义字典序中节点

class TrieNode{//定义结点
public:
    bool end;
    TrieNode *next[26];
    TrieNode(){
        end = false;
        memset(next, 0, sizeof(next));
    }
};

数据结构定义部分,end是用来判断是否存在该字符串。TrieNode *next[26] 表示26个字母,0表示a,25表示z。

1.2 初始化字典序

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

字典树创建一个头节点,设置为空状态。

1.3 在字典树中插入字符串

 void insert(string word) {//用来在字典树中插入一个单词
        TrieNode *now = root;
        for(int i = 0; i < word.size(); ++i){
            int child = word[i] - 'a';
            if(now->next[child] == nullptr){
                now->next[child] = new TrieNode();
            }
            now = now->next[child];
        }
        now->end = true;
    }

首先创建一个指针now指向头节点,然后开始从word的第一个字母开始到最后一个字母建立字典树中的节点。因为我们建立的字典树是26叉树,我们判断next[0] 表示指向字母为a的节点。首先先用一个int型整数child将字符转化为对应的数字,然后判断该26叉树中是否存在以child为节点的子树,没有的化则建立子树,并将now移动到相应的下一个位置。最终将now指针指向的end设置为true。

1.4 在字典树中查询字符串

 bool search(string word) {//查找一个单词是否在字典树中已经出现
        TrieNode *now = root;
        for(int i = 0; i < word.size(); ++i){
            int child = word[i] - 'a';
            if(now->next[child] == nullptr){
                return false;
            }
            now = now->next[child];
        }
        return now->end;
    }

按照建树的遍历思路来进行查询,一直对字符串从开始遍历到末尾,如果字母对应的字符在字典树中不存在(为空)则查询不到,查询到就移动到下一个节点。
最后判断now指针指向的end是否为true,如果为true则代表存在该字符串,否则不存在该字符串。

1.5 在字典树中判断字符串是否是已出现字符串的前缀

    bool startsWith(string prefix) {//用来判断该前缀是否是字典树中一个单词的前缀
        TrieNode *now = root;
        for(int i = 0; i < prefix.size(); ++i){
            int child = prefix[i] - 'a';
            if(now->next[child] == nullptr){
                return false;
            }
            now = now->next[child];
        }
        return true;
    }

直接按照上述建树或者查询字符串的思路来进行遍历,如果遍历到空节点则不存在该前缀,如果遍历完毕,则返回true(存在以该字符串为前缀的字符串)。

2、实战演练

1、 题号
208. 实现 Trie (前缀树)

2、题目链接
点击跳转到题目位置

3、题目描述
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

4、题目代码 (如果认真学习了上面的代码模块,是不是十分熟悉呢~)

class TrieNode{//定义结点
public:
    bool end;
    TrieNode *next[26];
    TrieNode(){
        end = false;
        memset(next, 0, sizeof(next));
    }
};

class Trie {
public:
    TrieNode *root;
    Trie() {
        root = new TrieNode();
    }
    
    void insert(string word) {//用来在字典树中插入一个单词
        TrieNode *now = root;
        for(int i = 0; i < word.size(); ++i){
            int child = word[i] - 'a';
            if(now->next[child] == nullptr){
                now->next[child] = new TrieNode();
            }
            now = now->next[child];
        }
        now->end = true;
    }
    
    bool search(string word) {//查找一个单词是否在字典树中已经出现
        TrieNode *now = root;
        for(int i = 0; i < word.size(); ++i){
            int child = word[i] - 'a';
            if(now->next[child] == nullptr){
                return false;
            }
            now = now->next[child];
        }
        return now->end;
    }
    
    bool startsWith(string prefix) {//用来判断该前缀是否是字典树中一个单词的前缀
        TrieNode *now = root;
        for(int i = 0; i < prefix.size(); ++i){
            int child = prefix[i] - 'a';
            if(now->next[child] == nullptr){
                return false;
            }
            now = now->next[child];
        }
        return true;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

5、题解
如果无法AC这道题目,可以重新去看字典树的模板,多思考一下,相信你一定会做出来的!

总结

以上就是字典树的基础内容了,走入字典树系列收录于算法与数据结构专栏,旨在让希望理解字典树的能迅速入门字典树,并且通过实战能熟练掌握字典树的一些基本用法与变式。本篇文章是基础中的基础,一定要掌握,后面几篇将会从实战来巩固相应知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值