字典树trie
目录
基本概念
结构特点
优势
代码举例
应用场景
复杂度分析
变种
基本概念
字典树,又称为前缀树或Trie树,是一种树形数据结构,专门用于高效地存储和检索字符串数据集中的键。它的名字来源于单词"retrieval"的中间几个字母,强调了其用于快速检索的特性。
结构特点
1.
节点结构 :每个节点包含多个子节点(通常对应字符集的大小),一个指向父节点的指针,以及一个标记表示是否为某个单词的结束。
2.
路径表示 :从根节点到任意节点的路径表示一个字符串前缀。
3.
共享前缀 :具有相同前缀的字符串在树中共享相同的路径,直到前缀结束。
4.
根节点 :不包含字符,仅作为所有字符串的起点。
优势
1.
高效检索 :查找时间与字符串长度相关,与数据集大小无关,时间复杂度为O(k),其中k是字符串长度。
2.
前缀共享 :节省存储空间,尤其是当有大量具有相同前缀的字符串时。
3.
前缀匹配 :很容易实现前缀搜索功能。
4.
排序便捷 :按字典序遍历树可以得到所有字符串的排序。
代码举例
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define ALPHABET_SIZE 26/*字典树节点*/
typedef struct TrieNode
{struct TrieNode *children[ALPHABET_SIZE];char character;bool isEndOfWord;} TrieNode;/*创建新节点*/
TrieNode *createTrieNode()
{TrieNode *node;node = malloc(sizeof(TrieNode));node->isEndOfWord = false;int i = 0;while (i < ALPHABET_SIZE){node->children[i] = NULL;i++;}return node;
}/*插入新单词到字典树*/
void insert(TrieNode *root, char *word)
{/*递归地添加单词*///如果单词的长度为0,则返回if ((strlen(word) - 1) != 0){char character = *word;if (root->children[character - 97] == NULL){TrieNode *node = NULL;node = createTrieNode();node->character = character;root->children[character - 97] = node;}word++;insert(root->children[character - 97], word);}else{root->isEndOfWord = true;}return;
}/*在字典树中搜索单词*/
TrieNode *search(TrieNode *root, char *word)
{TrieNode *temp;while (*word != '\0'){char character = *word;if (root->children[character - 97] != NULL){temp = root->children[character - 97];word++;root = temp;}else{printf("No possible words!!\n");return NULL;}}return root;
}/*打印数组*/
void printArray(char chars[], int len)
{int i;for (i = 0; i < len; i++){printf("%c", chars[i]);}printf("\n");
}/*返回所有相关单词*/
void printPathsRecur(TrieNode *node, char prefix[], int filledLen)
{if (node == NULL)return;prefix[filledLen] = node->character;filledLen++;if (node->isEndOfWord){printArray(prefix, filledLen);}int i;for (i = 0; i < ALPHABET_SIZE; i++){printPathsRecur(node->children[i], prefix, filledLen);}
}/*遍历字典树*/
void traverse(char prefix[], TrieNode *root)
{TrieNode *temp = NULL;temp = search(root, prefix);int j = 0;while (prefix[j] != '\0'){j++;}printPathsRecur(temp, prefix, j - 1);
}/*使用字典树文件演示*/#define NUMBER_OF_WORDS (354935)
#define INPUT_WORD_SIZE (100)/*用户输入单词*/
char *receiveInput(char *s)
{scanf("%99s", s);return s;
}int main()
{//读取字典文件int word_count = 0;char *words[NUMBER_OF_WORDS];FILE *fp = fopen("dictionary.txt", "r");if (fp == 0){fprintf(stderr, "Error while opening dictionary file");exit(1);}words[word_count] = malloc(INPUT_WORD_SIZE);while (fgets(words[word_count], INPUT_WORD_SIZE, fp)){word_count++;words[word_count] = malloc(INPUT_WORD_SIZE);}//插入新单词到字典树TrieNode *root = NULL;root = createTrieNode();int i;for (i = 0; i < NUMBER_OF_WORDS; i++){insert(root, words[i]);}while (1){printf("Enter keyword: ");char str[100];receiveInput(str);printf("\n==========================================================\n");printf("\n【********************* Possible Words ********************】\n");// Find the word through the Trietraverse(str, root);printf("\n==========================================================\n");}
}
运行结果:
ubuntu@ubuntu-virtual-machine:~/trie$ ./a.out
Enter keyword: good luck==========================================================********************* Possible Words ********************
good
goodby
goodbye
goodbyes
goodbys
goodeniaceous
gooder
gooders
goodhap
goodhearted
goodheartedly
goodheartedness
goodhumoredness
goodie
goodies
gooding
goodish
goodishness
goodless
goodlier
goodliest
goodlihead
goodlike
goodliness
goodly
goodman
goodmanship
goodmen
goodnaturedness
goodness
goodnesses
goodnight
goodrich
goods
goodship
goodsire
goodsome
goodtemperedness
goodwife
goodwilies
goodwill
goodwilled
goodwillie
goodwillies
goodwillit
goodwills
goodwilly
goodwily
goodwives
goody
goodyear
goodyish
goodyism
goodyness
goodys
goodyship==========================================================
Enter keyword:
==========================================================********************* Possible Words ********************
luck
lucked
lucken
luckful
luckie
luckier
luckies
luckiest
luckily
luckiness
lucking
luckless
lucklessly
lucklessness
luckly
lucks
lucky==========================================================
Enter keyword:
应用场景
1.
自动补全 :如搜索引擎、输入法的自动补全功能。
2.
拼写检查 :检测输入单词是否存在于词典中。
3.
IP路由 :用于快速查找路由表。
4.
单词游戏 :如Scrabble、Boggle等游戏中的单词验证。
5.
前缀匹配 :如DNA序列分析中的匹配问题。
复杂度分析
时间复杂度 :
插入:O(k),k为字符串长度
查找:O(k),k为字符串长度
删除:O(k),k为字符串长度
空间复杂度 :
最坏情况:O(n×k),n为字符串数量,k为平均字符串长度
最好情况:O(∑k),所有字符串共享尽可能多的前缀
变种
1.
压缩字典树 :合并只有一个子节点的节点,减少空间使用。
2.
二进制字典树 :用于存储二进制数据,常用于异或相关问题。
3.
后缀字典树 :用于存储字符串的所有后缀,用于字符串匹配问题。
字典树是一种非常高效的字符串处理数据结构,尤其适合于有大量共享前缀的字符串集合。尽管在某些情况下可能会占用较多内存,但其出色的检索效率使其在许多应用场景中成为首选。