作者:__lifanxin
鏈接:https://blog.csdn.net/A951860555/article/details/108716487
來源:CSDN
著作權歸作者所有,任何形式的轉載都請聯系作者。
基本概念
??字典樹,又稱單詞查找樹,Trie樹,常用于統計、排序和保存大量的字符串。它的優點是利用字符串的公共前綴來減少存儲空間以及查詢時間,可以最大限度的減少無謂的字符串比較。
??其基本特點如下:一個根節點起始,根節點不存儲字符,然后除根節點外每一個節點都只包含一個字符;將根節點沿著某一條路徑到葉子節點的所有字符排列起來即存儲的一個字符串或稱為詞組;另外就英文字母而言,如果不區分大小寫,那么一個節點最大的子節點數是26,且每個子節點包含的字符都不相同。
??基本操作有:插入、查找、刪除
代碼實現
??采用c語言實現,分為三個文件:
trie_types.h
包括字典樹的結構體定義和基本操作函數的聲明
trie.c
字典樹基本操作函數的實現
main.c
代碼測試
頭文件 trie_types.h
#ifndef TRIE_TYPE
#define TRIE_TYPE
#include <stdbool.h>
#define MAX 26
// 只考慮小寫,英文最多26個字母,即每個節點最多擁有26個子節點
// 可以靈活的在此結構中添加字段以實現程序的需求
typedef struct TrieNode {
char val; // 存儲單個字符
bool isEnd; // 標記單詞最后一個字符,即葉子節點
struct TrieNode *next[MAX];
} *Trie, TrieNode;
void init_trie(Trie *trie); // 初始化字典樹
void insert_trie(Trie *trie, const char *str); // 插入字符串
void search_trie(Trie *trie, const char *str); // 查找詞組是否在字典樹中
void delete_trie(Trie *trie); // 刪除字典樹
#endif
函數實現 trie.c
#include "trie_types.h"
#include <malloc.h>
#include <stdio.h>
static TrieNode *queue[1024]; // 數組實現隊列
static TrieNode *create_node(int val); // 創建新節點
static void traverse_trie(Trie *trie); // 廣度遍歷字典樹
static TrieNode *create_node(int val){
// 創建新節點
TrieNode *newNode;
newNode = (TrieNode *)malloc(sizeof(TrieNode));
newNode->val = val;
newNode->isEnd = false;
for (int i=0; i<MAX; i++){
newNode->next[i] = NULL;
}
return newNode;
}
static void traverse_trie(Trie *trie){
// 廣度優先遍歷
TrieNode *node = *trie;
int head = 0, tail = 0; // 隊列頭尾指針
queue[tail++] = node; // 頭節點入隊
while (head != tail){
node = queue[head++]; // 節點出隊
for (int i=0; i<MAX; i++){
if (node->next[i]){
queue[tail++] = node->next[i];
}
}
}
}
void init_trie(Trie *trie){
// 初始化一個空的字典樹
*trie = NULL;
}
void insert_trie(Trie *trie, const char *str){
// 插入單詞到字典樹中
TrieNode *node;
int i = 0, index = 0;
if (!*trie){
// 頭節點為空,先創建頭節點
*trie = create_node(0);
}
node = *trie;
while (str[i]){
/*
利用字符相減,使用index記錄節點的插入位置,
保證處于同一層次且相同的字符不會被重復插入
*/
index = str[i] - 'a';
if (!node->next[index]){
node->next[index] = create_node(str[i]);
}
node = node->next[index];
i++;
}
node->isEnd = true; // 單詞最后一個字母標記為true
}
void search_trie(Trie *trie, const char *str){
/*
查詢單詞是否在字典樹中,不包括含有相同前綴的
例如已插入:he,那么h和her都不算在字典樹中
*/
TrieNode *node = *trie;
int i = 0, index = 0;
if (!node){
// 判斷是否是空樹
fputs("Trie is null!\n", stdout);
return;
}
while(str[i]){
/*
比較str中的每一個字符,
直到走到字符數組結尾或者字典樹中不存在對應節點
*/
index = str[i] - 'a';
if (!node->next[index])
break;
node = node->next[index];
i++;
}
if (node->isEnd && !str[i]) {
printf("%s is exist!\n", str);
} else {
printf("%s is not exist!\n", str);
}
}
void delete_trie(Trie *trie){
// 釋放字典樹內存
int i = 0;
traverse_trie(trie); // 存儲字典樹節點指針到隊列中
while (queue[i]){
free(queue[i++]);
}
}
代碼測試 main.c
#include "trie_types.h"
#include <stdio.h>
int main(void){
// 測試字典樹
char str[][4] = {
"he",
"she",
"his"
};
char tStr[][4] = {
"he",
"she",
"his",
"her",
"hh",
"oo"
};
Trie *trie;
init_trie(trie); // 初始化
for (int i=0; i<3; i++)
// 插入
insert_trie(trie, str[i]);
for (int i=0; i<6; i++)
// 查找
search_trie(trie, tStr[i]);
delete_trie(trie); // 釋放
return 0;
}
測試結果
測試結果
??如上圖所示,上述代碼很好的實現了字典樹的初始化、插入、查詢以及刪除操作,能夠正確的查詢到被存儲在字典樹中的詞組;而具有相同前綴但沒有完整存儲在字典樹中的詞組將不會被查詢到。