版权声明:转载请注明 https://blog.csdn.net/li13168690086/article/details/81366718
写在开头:字典树,再社会也是棵树!
字典树,又名前缀树,洋名Trie,是一种树型数据结构。其特点就是专门用于存储字母单词(其实数字也行啦),占有空间大,但效率比哈希树稳定,孰优孰劣,视场景而定。
先考虑这样几个字符串{ "i", "pen", "apple", "pineapple", "app", "apap", "pine", "pine" },并将他们以字典树结构存储,如下图1所示:
观察上图,有一个根源结点(不存储数据),然后以字典的顺序存放第一行数据(每个字符串开头的字符),按照字符串长度依次存储,如果字符串所有的字符都存入了,就给最后一个字符做标记。如果需要统计一个字符串出现了几次,再给最后一个字符的标记不断加1即可。
我们存储的字符串有一些特点:有的具有相同前缀,如"app"和 "apap" 有 "ap",但在后面有分叉;还有的不仅具有相同前缀,而且在同一条路上,如 "pine" 和 "pineapple"。所以具有相同前缀的,其前缀都会在一条路上,因此这种结构也称前缀树。
最后,我们考虑查找问题。当我们存好所有数据后,再去查找就如同翻字典一样。首先找到要搜索字符串的首字母,然后,顺着它继续找下一个字母,直到搜索完最后一个。
我参考了神奕博主的代码实现,下面是转载其第四部分的代码 (加入了新的注释)。
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
#define ALPHABET_SIZE 26 //定义结点有26个大小(默认为26个小写字母,可随实际修改)
typedef struct trie_node
{
int count; // 标记,记录该节点代表的单词的个数
trie_node *children[ALPHABET_SIZE]; // 各个子节点
}*trie;
trie_node* create_trie_node()
{
trie_node* pNode = new trie_node(); //创建一棵新树结点
pNode->count = 0;
for(int i=0; i<ALPHABET_SIZE; ++i) //每个结点都有26个空间,默认为NULL
pNode->children[i] = NULL;
return pNode;
}
void trie_insert(trie root, char* key) //字符串插入函数
{
trie_node* node = root; //建立新树,root为根源结点,将root赋值给node
char* p = key; //新建p指针,并获得key字符串的首地址
while(*p) //遍历key字符串
{
if(node->children[*p-'a'] == NULL) //如果录入字符是新点,就创建新结点。
{
node->children[*p-'a'] = create_trie_node(); //创建
}
node = node->children[*p-'a']; //将node指向当前遍历结点
++p; //字符串指针地址加1,即获取下一个字符
}
node->count += 1; //最后一个字符,也是最后一个结点的标记加1
}
/**
* 查询:不存在返回0,存在返回出现的次数
*/
int trie_search(trie root, char* key) //字符串完全匹配,基础查询
{
trie_node* node = root; //获得根源结点root,赋值给node
char* p = key; //获取字符串首地址
while(*p && node!=NULL) //如果字符串遍历到'\0'(最后),或者字符所在的结点不存在,就退出查询
{
node = node->children[*p-'a']; //获取当前字符结点
++p; //字符串地址加1,获取下一个字符
}
if(node == NULL) //如果结点为空,意味着字符串有字符不存在于树中,即不存在此字符串。
return 0;
else
return node->count; //如果存在,就返回存在的次数,至少为1
}
int main()
{
// 关键字集合
char keys[][8] = {"the", "a", "there", "answer", "any", "by", "bye", "their"};
trie root = create_trie_node(); //新建根源结点
for(int i = 0; i < 8; i++) // 创建trie树
trie_insert(root, keys[i]);
// 检索字符串,测试
char s[][32] = {"Present in trie", "Not present in trie"};
printf("%s --- %s\n", "the", trie_search(root, "the")>0?s[0]:s[1]);
printf("%s --- %s\n", "these", trie_search(root, "these")>0?s[0]:s[1]);
printf("%s --- %s\n", "their", trie_search(root, "their")>0?s[0]:s[1]);
printf("%s --- %s\n", "thaw", trie_search(root, "thaw")>0?s[0]:s[1]);
return 0;
}
最后提醒注意,如果存入的只有小写字符,那么根据ascll码存放就是【字符-'a'】; 如果是数字则是 【字符-'0'】,大写字母类似,若为混合,也就是将范围再扩大,按实际需要编写。