字典树的基本知识及使用C语言的相关实现

概念

如果我们有and,as,at,cn,com这些关键词,那么trie树(字典树)是这样的:

从上面的图中,我们或多或少的可以发现一些好玩的特性。

第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符。

第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。

第三:每个单词的公共前缀作为一个字符节点保存。

使用范围

既然学Trie树,我们肯定要知道这玩意是用来干嘛的。

第一:词频统计。

可能有人要说了,词频统计简单啊,一个hash或者一个堆就可以打完收工,但问题来了,如果内存有限呢?还能这么

玩吗?所以这里我们就可以用trie树来压缩下空间,因为公共前缀都是用一个节点保存的。

第二: 前缀匹配

就拿上面的图来说吧,如果我想获取所有以"a"开头的字符串,从图中可以很明显的看到是:and,as,at,如果不用trie树,

你该怎么做呢?很显然朴素的做法时间复杂度为O(N2) ,那么用Trie树就不一样了,它可以做到h,h为你检索单词的长度,

可以说这是秒杀的效果。

数据结构定义

  #define MAX 26 // 字符集大小 

  typedef struct trieNode {
    struct trieNode *next[MAX];
    int count; // 记录该字符出现次数
  } trieNode;

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

实现方法
搜索字典项目的方法:

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

其他操作类似

实现模板

初始化根结点

  /**
   * 初始化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;
  }
(0)

相关推荐

  • 字典树的基本知识及使用C语言的相关实现

    概念 如果我们有and,as,at,cn,com这些关键词,那么trie树(字典树)是这样的: 从上面的图中,我们或多或少的可以发现一些好玩的特性. 第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符. 第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串. 第三:每个单词的公共前缀作为一个字符节点保存. 使用范围 既然学Trie树,我们肯定要知道这玩意是用来干嘛的. 第一:词频统计. 可能有人要说了,词频统计简单啊,一个hash或者一个堆就可以打完收工,但问题

  • python自然语言处理之字典树知识总结

    一.什么是字典树 在自然语言处理中,字符串集合常用字典树存储,这是一种字符串上的树形数据结构.字典树中每条边都对应一个字,从根节点往下的路径构成一个个字符串. 字典树并不直接在节点上存储字符串,而是将词语视作根节点到某节点之间的一条路径,并在终点节点上做个标记(表明到该节点就结束了). 要查询一个单词,指需要顺着这条路径从根节点往下走.如果能走到标记的节点,则说明该字符串在集合中,否则说明不在.下图为字典树结构示例: 如上图所示,每条路径都是一个词汇,且没有子节点就可以判定该条路径结尾了.具体可

  • PHP字典树(Trie树)定义与实现方法示例

    本文实例讲述了PHP字典树(Trie树)定义与实现方法.分享给大家供大家参考,具体如下: Trie树的概念(百度的解释):字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 我的理解是用来做字符串搜索的,每个节点只包含一个字符,比如录入单词"world",则树的结构

  • Trie树_字典树(字符串排序)简介及实现

    1.综述 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie树结构的优点在于:1) 不限制子节点的数量: 2) 自定义的输入序列化,突破了具体语言.应用的限制,成为一个通用的框架: 3) 可以进行最大Tokens序列长度的限制:4) 根据已定阈值输出重复的字符串:5) 提

  • c语言 树的基础知识(必看篇)

    第一.树的定义: 1.有且只有一个称为根的节点 2.有若干个互不相交的子树,这些子树本身也是一颗树 第二.专业术语: 树的深度:从根节点到最低层,节点的层数 ,称之为树的深度.  根节点是第一层 结点的层次:根节点为第一层,根节点的子节点为第2层,以此类推 叶子节点:没有子节点的节点 非终端节点:实际就是非叶子节点 结点度: 子节点的个数称为度树的度 第三.树的分类 一般树:任意一个节点的子节点的个数不受限制 二叉树:任意一个节点的子节点最多2个,且子节点的位置不可更改 满二叉树:在不增加层数的

  • 详解字典树Trie结构及其Python代码实现

    字典树(Trie)可以保存一些字符串->值的对应关系.基本上,它跟 Java 的 HashMap 功能相同,都是 key-value 映射,只不过 Trie 的 key 只能是字符串. Trie 的强大之处就在于它的时间复杂度.它的插入和查询时间复杂度都为 O(k) ,其中 k 为 key 的长度,与 Trie 中保存了多少个元素无关.Hash 表号称是 O(1) 的,但在计算 hash 的时候就肯定会是 O(k) ,而且还有碰撞之类的问题:Trie 的缺点是空间消耗很高. 至于Trie树的实现

  • Trie树(字典树)的介绍及Java实现

    简介 Trie树,又称为前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串.与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定.一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串. 它的主要特点如下: 根节点不包含字符,除根节点外的每一个节点都只包含一个字符. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串. 每个节点的所有子节点包含的字符都不相同. 如下是一棵典型的Trie树: Trie的来源是Retrie

  • Python实现简单字典树的方法

    本文实例讲述了Python实现简单字典树的方法.分享给大家供大家参考,具体如下: #coding=utf8 """代码实现了最简单的字典树,只支持由小写字母组成的字符串. 在此代码基础上扩展一下,就可以实现比较复杂的字典树,比如带统计数的,或支持更多字符的字典树, 或者是支持删除等操作. """ class TrieNode(object): def __init__(self): # 是否构成一个完成的单词 self.is_word = Fal

  • Python数据结构与算法之字典树实现方法示例

    本文实例讲述了Python数据结构与算法之字典树实现方法.分享给大家供大家参考,具体如下: class TrieTree(): def __init__(self): self.root = {} def addNode(self,str): # 树中每个结点(除根节点),包含到该结点的单词数,以及该结点后面出现字母的键 nowdict = self.root for i in range(len(str)): if str[i] not in nowdict: # 发现新的组合方式 nowdi

  • C#实现前向最大匹、字典树(分词、检索)的示例代码

    场景:现在有一个错词库,维护的是错词和正确词对应关系.比如:错词"我门"对应的正确词"我们".然后在用户输入的文字进行错词校验,需要判断输入的文字是否有错词,并找出错词以便提醒用户,并且可以显示出正确词以便用户确认,如果是错词就进行替换. 首先想到的就是取出错词List放在内存中,当用户输入完成后用错词List来foreach每个错词,然后查找输入的字符串中是否包含错词.这是一种有效的方法,并且能够实现.问题是错词的数量比较多,目前有10多万条,将来也会不断更新扩展

随机推荐