结构与实现
前缀树实现思路:
先建立一个空节点 指向字符串的首字母
有两个数来计数 pass和end。 每次使用某个字符就给这个字符的节点pass++ 在某个节点停下就给这个节点pass++, end++
如 abc 则 头节点指向a a pass++ b pass++ c pass++ end++
实现增删改查
其中删 必须要先查找是否存在
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ALPHABET_SIZE 26 //这里是为26个字母建的
//基本结构
struct TrieNode{
struct TrieNode* arr[ALPHABET_SIZE]; // 树里包含一个最大为26的数组
int passnum;
int endnum;
}TrieNode;
//建立节点
struct TrieNode* CreateNode(){ // 初始化一个新的TrieNode
struct TrieNode* node = (struct TrieNode*)malloc(sizeof(struct TrieNode));
node->passnum = 0;
node->endnum = 0;
for(int i=0; i<ALPHABET_SIZE; i++){
node->arr[i] = NULL:
}
return node;
}
常规操作
增删改查
增
插入操作,就是从root节点开始,看这个单词走哪条路径,一直走下去。沿途字母passnum++ 结尾字幕endnum++
void insert(struct TrieNode* root, char* word){
int len = strlen(word);
struct TrieNode* p = root;
for(int i=0; i<len; i++){
int index = word[i] - 'a'; //原本是a,b,c 现在用arr[0], arr[1]..来替代
if(!p->arr[index]){ //如果p往下指的指针 没有指向这个字母
p->arr[index] = CreateNode(); //建立
p->arr[index]->passnum++; //这个字母passnum++
p= p->arr[index];
}
p->endnum++; //标记一下单词结尾
}
查
有两种查询模式,一种就是正常的查询
正常查询
int search(struct TrieNode* root, char word){
if(word ==NULL){
return 0;
}
int len = strlen(word);
struct TrieNode* p = root;
int index =0;
for(int i=0; i<len; i++){
index = word[i] - 'a';
if(p->arr[index]==NULL){
return 0; //如果该节点不存在,说明这个单词不存在 直接返回
}
p= p->arr[index]; //如果该节点存在,继续往下遍历
}
return p->endnum; //最终遍历出单词出现了几次
}
前缀查询
还有一种是前缀树的特征,前缀遍历
实现思路是,只要节点的 passnum存在,就说明存在这个前缀
int PreSearch(struct TrieNode* root, char* pre){
if(pre ==NULL){
return 0;
}
struct TrieNode* p = root;
int len = strlen(pre);
for(int i= 0; i< len; i++){
int index = pre[i] - 'a';
if(p->arr[index] ==NULL){
return 0;
}
p = p->arr[index];
}
return p->passnum;
}
删
前缀树的删比较特殊,需要先判定这个单词在不在树里
不然本来 树里有 'abc' 想删'ab' 直接就出问题了...
void delete(struct TrieNode* root, char* word){
if(word ==NULL){
return;
}
if(research(root, word) == 0){
return; // 先看看word存不存在
}
struct TrieNode* p = root;
int len = strlen(word);
for(int i=0 ; i< len; i++){
int index = word[i] - 'a';
if(p->arr[index]->passnum == 1){ // 如果passnum只等于1
free(p->arr[index]); // free掉这个节点
p->arr[index] = NULL; // 指针变空
}
else{
p->arr[index]->passnum--; // 如果passnum大于1 就让沿途passnum--
p = p->arr[index]; // p指向下一节点
}
}
p->endnum--; // 终点的endnum --
}
总结
这里面感觉只有 index = word[i] - ' a' 这里比较难理解。