c语言 trie树,字典树的基本知识及使用C语言的相关实现

概念

如果我们有and,as,at,cn,com这些关键词,那么trie树(字典树)是这样的:

57de6327de75fbc174e42b8311e311f4.png

从上面的图中,我们或多或少的可以发现一些好玩的特性。

第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符。

第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。

第三:每个单词的公共前缀作为一个字符节点保存。

使用范围

既然学Trie树,我们肯定要知道这玩意是用来干嘛的。

第一:词频统计。

可能有人要说了,词频统计简单啊,一个hash或者一个堆就可以打完收工,但问题来了,如果内存有限呢?还能这么

玩吗?所以这里我们就可以用trie树来压缩下空间,因为公共前缀都是用一个节点保存的。

第二: 前缀匹配

就拿上面的图来说吧,如果我想获取所有以"a"开头的字符串,从图中可以很明显的看到是:and,as,at,如果不用trie树,

你该怎么做呢?很显然朴素的做法时间复杂度为O(N2) ,那么用Trie树就不一样了,它可以做到h,h为你检索单词的长度,

可以说这是秒杀的效果。

数据结构定义

#define MAX 26 // 字符集大小

typedef struct trieNode {

struct trieNode *next[MAX];

int count; // 记录该字符出现次数

} trieNode;

next数组表示每层有多少类的数,如果只是小写字母,26即可

实现方法搜索字典项目的方法:

从根节点开始一次搜索

获取要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索

在相应的子树上,获取要查找关键词的第二个字母,并进一步选择对应的子树进行检索

迭代过程

在某个节点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找

其他操作类似

实现模板

初始化根结点

/**

* 初始化Trie树根结点

*/

void initTrie(trieNode **root)

{

int i;

*root = (trieNode *)malloc(sizeof(trieNode));

(*root)->count = 0;

for (i = 0; i < MAX; i ++) {

(*root)->next[i] = NULL;

}

}

插入单词到trie树

/**

* Trie树插入操作

*/

void insert(char *str, trieNode *root)

{

int i;

trieNode *p = root;

while (*str != '\0') {

if (p->next[*str - 'a'] == NULL) {

trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));

for (i = 0; i < MAX; i ++) {

tmp->next[i] = NULL;

}

tmp->count = 1;

p->next[*str - 'a'] = tmp;

p = p->next[*str - 'a'];

} else {

p = p->next[*str - 'a'];

p->count ++;

}

str ++;

}

}

统计查找单词数量

/**

* 统计前缀出现次数

*/

int count(char *search, trieNode *root)

{

trieNode *p = root;

while (*search != '\0') {

if (p->next[*search - 'a'] == NULL) {

return 0;

} else {

p = p->next[*search - 'a'];

search ++;

}

}

return p->count;

}

清理trie树

/**

* 清理trie树

*/

void delTrie(trieNode *root)

{

int i;

for (i = 0; i < MAX; i ++) {

if (root->next[i] != NULL) {

delTrie(root->next[i]);

}

}

free(root);

}

时间复杂度

插入、查找的时间复杂度均为O(n),n为字符串的长度

空间复杂度较高,O(26^n),典型空间换时间

参考题目

ac代码:

#include

#include

#include

#define MAX 26 // 字符集大小

typedef struct trieNode {

struct trieNode *next[MAX];

int count; // 记录该字符出现次数

} trieNode;

/**

* 初始化Trie树根结点

*/

void initTrie(trieNode **root)

{

int i;

*root = (trieNode *)malloc(sizeof(trieNode));

(*root)->count = 0;

for (i = 0; i < MAX; i ++) {

(*root)->next[i] = NULL;

}

}

/**

* Trie树插入操作

*/

void insert(char *str, trieNode *root)

{

int i;

trieNode *p = root;

while (*str != '\0') {

if (p->next[*str - 'a'] == NULL) {

trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));

for (i = 0; i < MAX; i ++) {

tmp->next[i] = NULL;

}

tmp->count = 1;

p->next[*str - 'a'] = tmp;

p = p->next[*str - 'a'];

} else {

p = p->next[*str - 'a'];

p->count ++;

}

str ++;

}

}

/**

* 统计前缀出现次数

*/

int count(char *search, trieNode *root)

{

trieNode *p = root;

while (*search != '\0') {

if (p->next[*search - 'a'] == NULL) {

return 0;

} else {

p = p->next[*search - 'a'];

search ++;

}

}

return p->count;

}

/**

* 清理trie树

*/

void delTrie(trieNode *root)

{

int i;

for (i = 0; i < MAX; i ++) {

if (root->next[i] != NULL) {

delTrie(root->next[i]);

}

}

free(root);

}

int main(void)

{

char str[15];

trieNode *root;

// 初始化根结点

initTrie(&root);

while (gets(str) && str[0] != '\0') {

// 插入Trie树

insert(str, root);

}

// 查找前缀出现次数

while (gets(str) && str[0] != '\0') {

printf("%d\n", count(str, root));

}

delTrie(root);

return 0;

}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 C 语言字典 Trie 的代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_WORD_LEN 100 typedef struct TrieNode { char value; struct TrieNode *children[26]; int is_end; } TrieNode; TrieNode *createNode(char value) { TrieNode *node = (TrieNode *) malloc(sizeof(TrieNode)); node->value = value; for (int i = 0; i < 26; i++) { node->children[i] = NULL; } node->is_end = 0; return node; } void insert(TrieNode *root, char *word) { TrieNode *curr = root; int len = strlen(word); for (int i = 0; i < len; i++) { int index = word[i] - 'a'; if (curr->children[index] == NULL) { curr->children[index] = createNode(word[i]); } curr = curr->children[index]; } curr->is_end = 1; } int search(TrieNode *root, char *word) { TrieNode *curr = root; int len = strlen(word); for (int i = 0; i < len; i++) { int index = word[i] - 'a'; if (curr->children[index] == NULL) { return 0; } curr = curr->children[index]; } return curr->is_end; } int main() { TrieNode *root = createNode('\0'); char word[MAX_WORD_LEN]; int choice = 0; do { printf("1. Insert Word\n"); printf("2. Search Word\n"); printf("3. Exit\n"); printf("Enter Choice: "); scanf("%d", &choice); switch (choice) { case 1: printf("Enter Word to Insert: "); scanf("%s", word); insert(root, word); break; case 2: printf("Enter Word to Search: "); scanf("%s", word); if (search(root, word)) { printf("%s is present in the dictionary.\n", word); } else { printf("%s is not present in the dictionary.\n", word); } break; case 3: printf("Exiting...\n"); break; default: printf("Invalid Choice!\n"); break; } } while (choice != 3); return 0; } ``` 该实现使用了一个 TrieNode 结构体来表示 Trie 中的每个节点,其中包含了节点的值,子节点指针数组和一个标志位,用于指示该节点是否为单词的结尾。 在插入单词时,从根节点开始遍历 Trie ,如果当前节点的相应子节点为空,则新建一个节点并将其作为当前节点的相应子节点。最后将单词的结尾节点的标志位设置为 1。 在查找单词时,同样从根节点开始遍历 Trie ,如果当前节点的相应子节点为空,则说明该单词不存在于 Trie 中。如果单词的最后一个字符所在的节点的标志位为 1,则说明该单词存在于 Trie 中。 该实现中还包含了一个简单的命令行界面,用于接收用户的输入并执行相应的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值