又称为字典树、单词查找树。是一种树形结构,哈希树的变种。典型应用是用于统计、排序和保存大量的字符串(不仅仅限于字符串),经常被搜索引擎系统用于文本词频统计。trie树是用空间换取时间的典型数据结构。
根节点不包含字符,除了根节点外,每个节点都只包含一个字符;
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
每个节点的所有子节点包含的字符都不相同。
基本操作有:树的创建、删除;节点的插入、查找、删除(好像这种操作比较少见)。
利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高
每个节点需要包含一些额外的字段来保存节点信息,浪费空间。
串的快速检索、“串”排序、最长公共前缀
本文实现了trie树的创建、删除;字符串的插入、查找、删除。另外,本文限定单词仅仅由26个小写字母组成。具体实现将在本文最后给出完整代码。
typedef struct TrieNode { int isStr; /*word flag*/ int elemcount; /*word number*/ int passcount; /*pass by number*/ struct TrieNode *next[MAX]; }Trie;
其中,isStr 单词结束标记;elemcount是单词被插入的次数;passcount是所有单词经过该节点的次数;next是该节点的孩子节点。
创建trie树与trie树初始化,分开实现。
Trie * trie_malloc(); void trie_init(Trie *p);
删除整棵树,释放其拥有的所有资源
void trie_del(Trie *root);
将字符串s插入trie树root
void trie_insert(Trie *root,const char *s);
从trie树中查找字符串s,如果能找到则返回s在trie中出现的次数(即被插入的次数),否则返回0
int trie_search(Trie *root,const char *s);
trie树中删除字符串s(s是整个单词,并不会删除一个完整单词的一部分)
void trie_node_del(Trie *root, const char *s);
这些辅助函数相当于C++的private成员,仅仅用来辅助实现基本的用户功能。分别为:验证字符串的合法性、根据字符获取在孩子节点中的索引、递归删除trie树、递归删除字符串对应的节点
static int verify_str(const char *s); static int get_index(char ch); static void delete_subtrie(Trie **root); static void delete_node(Trie **node, const char *chr);
具体的代码实现trie.h如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 26 #define IDXERR -1 #define INVALID 0 #define VALID 1 #define true 1 #define false 0 typedef struct TrieNode { int isStr; /*word flag*/ int elemcount; /*word number*/ int passcount; /*pass by number*/ struct TrieNode *next[MAX]; }Trie; Trie * trie_malloc(); void trie_init(Trie *p); void trie_insert(Trie *root,const char *s); int trie_search(Trie *root,const char *s); void trie_del(Trie *root); void trie_node_del(Trie *root, const char *s); static int verify_str(const char *s); static int get_index(char ch); static void delete_subtrie(Trie **root); static void delete_node(Trie **node, const char *chr); void trie_init(Trie *p) { int i=0; if(NULL == p) return; memset(p,0x0,sizeof(Trie)); for(i=0;i<MAX;i++) { p->next[i] = NULL; } p->isStr = false; p->elemcount = 0; p->passcount = 0; } Trie * trie_malloc() { Trie *temp=(Trie *)malloc(sizeof(Trie)); return temp; } void trie_insert(Trie *root,const char *s) { if(NULL == root || NULL == s || *s == '\0') return; if(INVALID == verify_str(s)) return; Trie *p = root; int idx = 0; while(*s!='\0') { idx = get_index(*s); if(NULL == p->next[idx]) { Trie *temp = trie_malloc(); trie_init(temp); p->next[idx]=temp; p=p->next[idx]; } else { p=p->next[idx]; } s++; p->passcount++; } p->isStr=true; p->elemcount++; } int trie_search(Trie *root,const char *s) { Trie *p=root; int idx = 0; if(NULL == root || NULL == s) return 0; while(p != NULL && *s != '\0') { idx = get_index(*s); if(IDXERR == idx) return 0; p=p->next[idx]; s++; } if(p != NULL && true == p->isStr) return p->elemcount; else return 0; } void trie_del(Trie *root) { if(NULL == root) return; delete_subtrie(&root); } static int verify_str(const char *s) { if(NULL == s) return INVALID; while(*s!='\0') { if(IDXERR == get_index(*s)) return INVALID; s++; } return VALID; } static int get_index(char ch) { int idx = ch-'a'; if(idx < 0 || idx >= MAX) { idx = IDXERR; } return idx; } static void delete_subtrie(Trie **root) { int i = 0; if(root != NULL && *root != NULL) { for(i = 0; i < MAX; i++) { if((*root)->next[i]!=NULL) { delete_subtrie(&((*root)->next[i])); } } } if(NULL != root) { free(*root); *root = NULL; } } static void delete_node(Trie **node, const char *chr) { int idx = 0; if(NULL == node || NULL == *node || NULL == chr) return; if(*chr != '\0'&& *(chr+1) != '\0' && *node != NULL) { chr++; idx = get_index(*chr); delete_node(&((*node)->next[idx]), chr); } if(*node != NULL) { free(*node); *node = NULL; } return; } void trie_node_del(Trie *root, const char *s) { if(NULL == root || NULL == s || *s == '\0') return; int elemcount=0; Trie *p = root; Trie *q = p; int remain=0; int idx = 0; if((elemcount=trie_search(root,s))<= 0) return; while(*s != '\0' && p != NULL) { idx = get_index(*s); q = p; p = p->next[idx]; remain = p->passcount - elemcount; if(remain <= 0) { delete_node(&p, s); q->next[idx] = p; // important p = NULL;// can delete "p = NULL" break; } else { p->passcount = remain; } s++; } /*clear word flag*/ if(p != NULL) { p->isStr = false; } }
测试代码main.c
#include "trie.h" #define STR1 "hello" #define STR2 "hell" #define STR3 "helloworld" #define STR4 "world" #define STARLINE "****" int main(int argc, char *argv[]) { Trie *root= trie_malloc(); if(NULL == root) { printf("malloc trie failed\n"); exit(0); } trie_init(root); /*insert*/ printf("\n %sinsert string%s\n",STARLINE,STARLINE); trie_insert(root,STR1); printf("%s\n",STR1); trie_insert(root,STR2); printf("%s\n",STR2); trie_insert(root,STR3); printf("%s\n",STR3); trie_insert(root,STR4); printf("%s\n",STR4); trie_insert(root,STR2); printf("%s\n",STR2); /*search*/ printf("\n %ssearch string%s\n",STARLINE,STARLINE); printf("%s, %d times\n",STR1, trie_search(root,STR1)); printf("%s, %d times\n",STR2, trie_search(root,STR2)); printf("%s, %d times\n",STR3, trie_search(root,STR3)); printf("%s, %d times\n",STR4, trie_search(root,STR4)); /*delete*/ printf("\n %sdelete string%s\n",STARLINE,STARLINE); trie_node_del(root, STR1); printf("%s\n", STR1); printf("search %s, %d times\n",STR1, trie_search(root,STR1)); trie_node_del(root, STR2); printf("%s\n", STR2); printf("search %s, %d times\n",STR2, trie_search(root,STR2)); trie_node_del(root, STR3); printf("%s\n", STR3); printf("search %s, %d times\n",STR3, trie_search(root,STR3)); trie_node_del(root, STR4); printf("%s\n", STR4); printf("search %s, %d times\n",STR4, trie_search(root,STR4)); /*free trie*/ printf("\n %sfree trie%s\n",STARLINE,STARLINE); trie_del(root); root = NULL; return 0; }
gcc -g -o test main,c
执行结果如下图: