作者:__lifanxin
链接:https://blog.csdn.net/A951860555/article/details/108716487
来源:CSDN
著作权归作者所有,任何形式的转载都请联系作者。
基本概念
字典树,又称单词查找树,Trie树,常用于统计、排序和保存大量的字符串。它的优点是利用字符串的公共前缀来减少存储空间以及查询时间,可以最大限度的减少无谓的字符串比较。
其基本特点如下:一个根节点起始,根节点不存储字符,然后除根节点外每一个节点都只包含一个字符;将根节点沿着某一条路径到叶子节点的所有字符排列起来即存储的一个字符串或称为词组;另外就英文字母而言,如果不区分大小写,那么一个节点最大的子节点数是26,且每个子节点包含的字符都不相同。
基本操作有:插入、查找、删除
代码实现
采用c语言实现,分为三个文件:
trie_types.h
包括字典树的结构体定义和基本操作函数的声明
trie.c
字典树基本操作函数的实现
main.c
代码测试
头文件 trie_types.h
#ifndef TRIE_TYPE
#define TRIE_TYPE
#include <stdbool.h>
#define MAX 26
// 只考虑小写,英文最多26个字母,即每个节点最多拥有26个子节点
// 可以灵活的在此结构中添加字段以实现程序的需求
typedef struct TrieNode {
char val; // 存储单个字符
bool isEnd; // 标记单词最后一个字符,即叶子节点
struct TrieNode *next[MAX];
} *Trie, TrieNode;
void init_trie(Trie *trie); // 初始化字典树
void insert_trie(Trie *trie, const char *str); // 插入字符串
void search_trie(Trie *trie, const char *str); // 查找词组是否在字典树中
void delete_trie(Trie *trie); // 删除字典树
#endif
函数实现 trie.c
#include "trie_types.h"
#include <malloc.h>
#include <stdio.h>
static TrieNode *queue[1024]; // 数组实现队列
static TrieNode *create_node(int val); // 创建新节点
static void traverse_trie(Trie *trie); // 广度遍历字典树
static TrieNode *create_node(int val){
// 创建新节点
TrieNode *newNode;
newNode = (TrieNode *)malloc(sizeof(TrieNode));
newNode->val = val;
newNode->isEnd = false;
for (int i=0; i<MAX; i++){
newNode->next[i] = NULL;
}
return newNode;
}
static void traverse_trie(Trie *trie){
// 广度优先遍历
TrieNode *node = *trie;
int head = 0, tail = 0; // 队列头尾指针
queue[tail++] = node; // 头节点入队
while (head != tail){
node = queue[head++]; // 节点出队
for (int i=0; i<MAX; i++){
if (node->next[i]){
queue[tail++] = node->next[i];
}
}
}
}
void init_trie(Trie *trie){
// 初始化一个空的字典树
*trie = NULL;
}
void insert_trie(Trie *trie, const char *str){
// 插入单词到字典树中
TrieNode *node;
int i = 0, index = 0;
if (!*trie){
// 头节点为空,先创建头节点
*trie = create_node(0);
}
node = *trie;
while (str[i]){
/*
利用字符相减,使用index记录节点的插入位置,
保证处于同一层次且相同的字符不会被重复插入
*/
index = str[i] - 'a';
if (!node->next[index]){
node->next[index] = create_node(str[i]);
}
node = node->next[index];
i++;
}
node->isEnd = true; // 单词最后一个字母标记为true
}
void search_trie(Trie *trie, const char *str){
/*
查询单词是否在字典树中,不包括含有相同前缀的
例如已插入:he,那么h和her都不算在字典树中
*/
TrieNode *node = *trie;
int i = 0, index = 0;
if (!node){
// 判断是否是空树
fputs("Trie is null!\n", stdout);
return;
}
while(str[i]){
/*
比较str中的每一个字符,
直到走到字符数组结尾或者字典树中不存在对应节点
*/
index = str[i] - 'a';
if (!node->next[index])
break;
node = node->next[index];
i++;
}
if (node->isEnd && !str[i]) {
printf("%s is exist!\n", str);
} else {
printf("%s is not exist!\n", str);
}
}
void delete_trie(Trie *trie){
// 释放字典树内存
int i = 0;
traverse_trie(trie); // 存储字典树节点指针到队列中
while (queue[i]){
free(queue[i++]);
}
}
代码测试 main.c
#include "trie_types.h"
#include <stdio.h>
int main(void){
// 测试字典树
char str[][4] = {
"he",
"she",
"his"
};
char tStr[][4] = {
"he",
"she",
"his",
"her",
"hh",
"oo"
};
Trie *trie;
init_trie(trie); // 初始化
for (int i=0; i<3; i++)
// 插入
insert_trie(trie, str[i]);
for (int i=0; i<6; i++)
// 查找
search_trie(trie, tStr[i]);
delete_trie(trie); // 释放
return 0;
}
测试结果
测试结果如上图所示,上述代码很好的实现了字典树的初始化、插入、查询以及删除操作,能够正确的查询到被存储在字典树中的词组;而具有相同前缀但没有完整存储在字典树中的词组将不会被查询到。
网友评论