字典树

来源:互联网 发布:迪杰斯特拉算法视频 编辑:程序博客网 时间:2024/05/22 03:37

概述

字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高

性质

字典树的三个性质:
  • 根节点不包含字符,根节点外每一个节点都只包含一个字符
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
  • 每个节点的所有子节点包含的字符都不相同


数据结构定义

#define MAX 26// 字符集大小typedef struct trieNode {struct trieNode *next[MAX];int count;// 记录该字符出现次数} trieNode;

next数组表示每层有多少类的数,如果只是小写字母,26即可


实现方法

搜索字典项目的方法:
  1. 从根节点开始一次搜索
  2. 获取要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索
  3. 在相应的子树上,获取要查找关键词的第二个字母,并进一步选择对应的子树进行检索
  4. 迭代过程
  5. 在某个节点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找

其他操作类似


实现模板


初始化根结点

/** * 初始化Trie树根结点 */void initTrie(trieNode **root){int i;*root = (trieNode *)malloc(sizeof(trieNode));(*root)->count = 0;for (i = 0; i < MAX; i ++) {(*root)->next[i] = NULL;}}


插入单词到trie树

/** * Trie树插入操作 */void insert(char *str, trieNode *root){int i;trieNode *p = root;while (*str != '\0') {if (p->next[*str - 'a'] == NULL) {trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));for (i = 0; i < MAX; i ++) {tmp->next[i] = NULL;}tmp->count = 1;p->next[*str - 'a'] = tmp;p = p->next[*str - 'a'];} else {p = p->next[*str - 'a'];p->count ++;}str ++;}}


统计查找单词数量

/** * 统计前缀出现次数 */int count(char *search, trieNode *root){trieNode *p = root;while (*search != '\0') {if (p->next[*search - 'a'] == NULL) {return 0;} else {p = p->next[*search - 'a'];search ++;}}return p->count;}

清理trie树

/** * 清理trie树 */void delTrie(trieNode *root){int i;for (i = 0; i < MAX; i ++) {if (root->next[i] != NULL) {delTrie(root->next[i]);}}free(root);}


时间复杂度

插入、查找的时间复杂度均为O(n),n为字符串的长度

空间复杂度较高,O(26^n),典型空间换时间


参考题目

链接:统计难题

ac代码:

#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX 26// 字符集大小typedef struct trieNode {struct trieNode *next[MAX];int count;// 记录该字符出现次数} trieNode;/** * 初始化Trie树根结点 */void initTrie(trieNode **root){int i;*root = (trieNode *)malloc(sizeof(trieNode));(*root)->count = 0;for (i = 0; i < MAX; i ++) {(*root)->next[i] = NULL;}}/** * Trie树插入操作 */void insert(char *str, trieNode *root){int i;trieNode *p = root;while (*str != '\0') {if (p->next[*str - 'a'] == NULL) {trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));for (i = 0; i < MAX; i ++) {tmp->next[i] = NULL;}tmp->count = 1;p->next[*str - 'a'] = tmp;p = p->next[*str - 'a'];} else {p = p->next[*str - 'a'];p->count ++;}str ++;}}/** * 统计前缀出现次数 */int count(char *search, trieNode *root){trieNode *p = root;while (*search != '\0') {if (p->next[*search - 'a'] == NULL) {return 0;} else {p = p->next[*search - 'a'];search ++;}}return p->count;}/** * 清理trie树 */void delTrie(trieNode *root){int i;for (i = 0; i < MAX; i ++) {if (root->next[i] != NULL) {delTrie(root->next[i]);}}free(root);}int main(void){char str[15];trieNode *root;// 初始化根结点initTrie(&root);while (gets(str) && str[0] != '\0') {// 插入Trie树insert(str, root);}// 查找前缀出现次数while (gets(str) && str[0] != '\0') {printf("%d\n", count(str, root));}delTrie(root);return 0;}


后记

其实博主写这篇博客,也是因为电话面试涂鸦移动的时候被问到了智能提示,其实智能提示实现不难,trie树+top k,中文的话转成拼音存储即可,我当时虽然回答出来了,但是连连看的题目没回答上,蛋疼