数据结构 - 字典树(Trie)

定义

Trie又称字典树,前缀树,通过下图可以得出Trie的特点:

  • Trie的根结点是空的
  • 除根结点外,每个结点储存一个字母
  • 从根结点到每个字母结点的路径上的所有字母连接而成的字符串就是该结点对应的字符串
  • 每个非叶子结点一般都会被多次使用,以节省遍历的时间效率

在这里插入图片描述

原理

Trie结点

Trie结点即树上每一个结点,需要维护两个信息:

  • 下一个字符指向的结点编号
  • 结点附加的值(即当前字符串附加的值)
struct TrieNode {
    int val;        // 每个字符串附加的值
    int next[26];   // 下一个字符的结点编号
} trie[MAXN];

插入操作

当插入一个新的字符串时,从左向右扫描每一个字符,在树中不断向下游走。

void insert(const char* str, int val) {
    const char* cur = str;
    int node = 0;
    while (*cur) {
        int ch = *cur - 'a';
        int& _next = trie[node].next[ch];
        if (!_next) {
            ++nodes_cnt;
            _next = nodes_cnt;
        }
        node = _next;
        ++cur;
    }
    trie[node].val = val;
}

查询操作

查询某个字符串在不在树中,步骤与插入操作类似,返回该字符串对应的结点编号,若不存在则返回0。

int search(const char* str) {
    const char* cur = str;
    int node = 0;
    while (*cur) {
        int ch = *cur - 'a';
        node = trie[node].next[ch];
        if (!node)
            return 0;
        ++cur;
    }
    return node;
}

例题:正则查询

LeetCode 211
字典树的插入和查询操作,但是查询字符串可以含有正则表达式“.”,匹配任意单个字符。

思路:对于待查询字符串,从左到右,递归地查询,对于“.”,立刻产生26个新的递归分支。:请留意每个结点维护的valid值,它是必不可少的,表明当前字符串是否存在。如果不使用valid的话,假如插入了“abc”,那么查询“ab”结果也为真。

#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <map>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <cassert>
using namespace std;

const int MAXN = 500005;  // MAXN的大小应设为字符串数量×字符串最大长度

struct TrieNode {
    bool valid;     // 该字符串是否存在
    int next[26];   // 下一个字符的结点编号
} trie[MAXN];
int nodes_cnt = 0;  // 当前结点数量

void insert(const string& str) {
    int node = 0;
    for (char _ch : str) {
        int ch = _ch - 'a';
        int& _next = trie[node].next[ch];
        if (!_next) {
            ++nodes_cnt;
            _next = nodes_cnt;
        }
        node = _next;
    }
    trie[node].valid = true;
}

bool find(const string& str, int idx, char target, int node) {
    if (target == '.') {
        for (char ch = 'a'; ch <= 'z'; ++ch) {
            if (find(str, idx, ch, node)) {
                return true;
            }
        }
        return false;
    } else {
        int len = str.length();
        int ch = target - 'a';
        node = trie[node].next[ch];
        if (!node)
            return false;
        if (idx >= len - 1)
            return trie[node].valid;
        return find(str, idx + 1, str[idx + 1], node);
    }
}

class WordDictionary {
public:
    /** Initialize your data structure here. */
    WordDictionary() {
        memset(trie, 0, sizeof(trie));
        nodes_cnt = 0;
    }

    /** Adds a word into the data structure. */
    void addWord(string word) {
        insert(word);
    }

    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    bool search(string word) {
        return find(word, 0, word[0], 0);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值