深入理解Trie树:高效处理字符串的利器

1. 什么是Trie树?

Trie树(字典树、前缀树)是一种树形数据结构,专门用于高效存储和检索字符串集合。它的核心特点是:

  • 每个节点代表一个字符。

  • 从根节点到某一节点的路径构成一个字符串。

  • 适用于前缀匹配、词频统计、自动补全等场景。


2. Trie树的优势

操作 时间复杂度 适用场景
插入字符串 O(L) 动态添加新字符串
查询字符串 O(L) 快速检查字符串是否存在
前缀匹配 O(L) 搜索引擎、输入法自动补全

L 为字符串长度)

3. Trie树的代码实现(C语言)

3.1 数据结构定义

#define N 100010  // 最大节点数

int con[N][26];  // 子节点数组(a-z映射到0-25)
int cnt[N];      // 记录以节点p结尾的字符串数量
int idx;         // 当前可用的节点编号
  • con[p][u]:节点 p 的第 u 个子节点的编号(u = a[i] - 'a')。

  • cnt[p]:以节点 p 结尾的字符串的出现次数。

  • idx:动态分配节点编号,初始为 0(根节点)。


3.2 插入字符串 insert(char a[])

void insert(char a[]) {
    int p = 0;  // 从根节点开始
    for (int i = 0; a[i]; i++) {
        int u = a[i] - 'a';  // 字符转为数字(a→0, b→1, ..., z→25)
        if (!con[p][u]) {    // 如果子节点不存在
            con[p][u] = ++idx; // 创建新节点
        }
        p = con[p][u];       // 移动到子节点
    }
    cnt[p]++;  // 标记字符串结尾,增加计数
}

图解插入 "abc"

  1. 初始化:p = 0(根节点)。

  2. 'a' → u=0,创建 con[0][0] = 1p=1

  3. 'b' → u=1,创建 con[1][1] = 2p=2

  4. 'c' → u=2,创建 con[2][2] = 3p=3

  5. cnt[3]++,表示 "abc" 出现一次。


3.3 查询字符串 query(char a[])

int query(char a[]) {
    int p = 0;
    for (int i = 0; a[i]; i++) {
        int u = a[i] - 'a';
        if (!con[p][u]) return 0;  // 子节点不存在,字符串不存在
        p = con[p][u];             // 移动到子节点
    }
    return cnt[p];  // 返回字符串出现次数
}

图解查询 "ab"

  1. p=0 → 'a' → u=0 → p=1

  2. 'b' → u=1 → p=2

  3. 返回 cnt[2]"ab" 的出现次数)。


4. 完整代码示例

//高效地插入字符串和查询字符串出现的次数

#include 
#include 
#define N 100010

/*
	con[p][u]:表示节点p的第u个子节点的编号(u=0-25对应a-z)
	cnt[p]:记录以节点p结尾的字符串的出现次数
	idx:动态分配节点编号,初始为0(根节点)
*/
int con[N][26];		//Trie树的节点,每个节点最多26个子节点(对应小写字母a-z)
int cnt[N];			//记录以节点p结尾的字符串数量
int idx;			//当前可用的节点编号

//插入函数
void insert(char a[])
{
	int p = 0;		//从根节点开始
	for (int i = 0;a[i]; i++)
	{
		int u = a[i] - 'a';		//将字符转换为0-25的数字
		
		//如果子节点不存在
		if (!con[p][u])
		{
			//创建新节点
			con[p][u] = ++idx;
		}
		//移动到子节点
		p = con[p][u];
	}
	//标记字符串结尾,并增加计数
	cnt[p]++;
}

//查询函数
int query(char a[])
{
	int p = 0;
	for (int i = 0;a[i]; i++)
	{
		int u = a[i] - 'a';
		
		//如果子节点不存在,说明字符串不存在
		if (!con[p][u])
		{
			return 0;
		}

		//移动到子节点
		p = con[p][u];
	}

	//返回以p结尾的字符串的出现次数
	return cnt[p];
}

int main()
{
	//操作次数
	int n;
	scanf("%d", &n);

	while (n--)
	{
		//读取操作类型和字符串
		char c, a[N];
		scanf("\n%c%s", &c, a);

		//插入
		if (c == 'I')
		{
			insert(a);
		}
		//查询
		else if (c == 'Q')
		{
			//输出查询字符串出现的次数
			printf("%d\n", query(a));
		}
	}

	return 0;
}

5. 实际应用场景

5.1 词频统计

  • 输入:大量单词(如 ["apple", "banana", "apple"])。

  • 输出{"apple":2, "banana":1}

5.2 前缀自动补全

  • 输入:输入 "app"

  • 输出:匹配所有以 "app" 开头的单词(如 "apple""appetite")。

5.3 敏感词过滤

  • 构建敏感词 Trie 树,快速检测文本中的敏感词。


  • Trie树是一种高效处理字符串的数据结构,适用于插入、查询、前缀匹配

  • 时间复杂度:插入和查询均为 O(L)L 为字符串长度)。

  • 空间优化:可改用哈希表或动态分配内存(如 C++ 的 unordered_map)。

掌握 Trie 树后,你可以轻松解决:

  • LeetCode 208. 实现 Trie

  • LeetCode 212. 单词搜索 II

你可能感兴趣的:(算法与数据结构,算法,数据结构)