字典树通俗的叫法有:Trie树、单词查找树、前缀树,是一种哈希树的变种
为什么说非典型呢?因为它和一般的多叉树不一样,尤其在结点的数据结构设计上,
结构体差异
一般的多叉树的结点是这样的
typedef struct TreeNode {
VALUETYPE value; //结点值
TreeNode* children[NUM]; //指向孩子结点
}TreeNode;
字典树的结构体是这样的
typedef struct TrieNode {
bool isEnd; //该结点是否是一个串的结束
TrieNode* children[26]; //字母映射表
}TrieNode;
由两个结构体相比可以看出差异 ,接下来我们一起看看字母映射表
字母映射表
以 "sea","sells","she"为例,看下这三个单词在字典树中是如何存储的
省去上图中的空指针,可以画出如下图像
“sea”:“a”的父节点是“e”,“e”的父节点是 “s”,前缀树、字典树的名字由此而来
字典树的操作
结构体
typedef struct Trie {
struct Trie *children[26];
bool isEnd;
} Trie;
创建
Trie *trieCreate()
{
Trie *trie = (Trie *)malloc(sizeof(Trie));
memset(trie->children, 0, sizeof(trie->children));
trie->isEnd = false;
return trie;
}
插入
void trieInsert(Trie *obj, char *word)
{
int wordLength = strlen(word);
Trie *trieP = obj;
for (int i = 0; i < wordLength; i++) {
int wordInd = word[i] - 'a';
if (trieP->children[wordInd] == 0) {
trieP->children[wordInd] = trieCreate();
}
trieP = trieP->children[wordInd];
}
trieP->isEnd = true;
}
查询
bool trieSearch(Trie *obj, char *word)
{
int wordLength = strlen(word);
Trie *trieP = obj;
for (int i = 0; i < wordLength; i++) {
int wordInd = word[i] - 'a';
if (trieP->children[wordInd] == NULL) {
return false;
}
trieP = trieP->children[wordInd];
}
return trieP->isEnd;
}
前缀查询
bool trieStartsWith(Trie *obj, char *prefix)
{
int wordLength = strlen(prefix);
Trie *trieP = obj;
for (int i = 0; i < wordLength; i++) {
int wordInd = prefix[i] - 'a';
if (trieP->children[wordInd] == NULL) {
return false;
}
trieP = trieP->children[wordInd];
}
return true;
}
释放
void trieFree(Trie *obj)
{
for (int i = 0; i < 26; i++) {
if (obj->children[i]) {
trieFree(obj->children[i]);
}
}
free(obj);
}