[数据结构] 字典树

来源:互联网 发布:手机淘宝网2016 编辑:程序博客网 时间:2024/05/21 21:38

字典树又被称作Trie树,其效率非常高,所以在字符串查找,前缀匹配中应用非常广泛,其高效率是以空间为代价的。典型应用是用于统计和排序大量的字符串,它的优点是,最大限度地减少无谓的字符串比较,查询效率比哈希表更高。

Trie树的核心思想是以空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

Trie树的基本性质可以归纳为:
- 根节点不包含字符,除根节点以外,每个节点只包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,就是该节点对应的字符串。
- 每个节点的所有子节点包含的字符串不相同。

Trie树有一些特性:
- 如果字符的种类数量为n,那么每个结点的出度为n,这也是空间换时间的体现,浪费了很多的空间。
- 插入查找的复杂度为O(n),n为字符串长度。

基本思想:
1、插入过程:
对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支往下走,直到单词遍历完,将最后的节点标记为红色,表示该单词已经插入Trie树。
2、查询
同样的,从根开始单词的字母顺序向下遍历Trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未标记为红色,则表示单词不存在;若最后的节点标记为红色,表示该单词存在。

二、Trie树的数据结构:
下面以英文单词构建的字典树为例,假设所有统计的字母都是小写字母,那么这棵Trie树中,每个节点包括26个孩子节点。
声明一个包含Trie树的节点信息的结构体:

struct Trie{    struct Trie* next[26];  //指向各个子树的指针    bool isWord;             //标记该节点处是否能构成单词}Root;

这里写图片描述

Trie树的操作:
在Trie树中主要有三个操作,插入、查找和删除。一般情况下Trie树很少存在单独删除某个节点的情况,因此只考虑删除整棵树。

1、插入
假设存在字符串str,Trie树的根节点为root。i=0,p= root。
1)取str[i],判断p->next[str[i]-‘a’]是否为空,若为空,则建立节点temp,并将p->next[str[i]-‘a’]指向temp,然后p指向temp;
若不为空,则p = p->next[str[i]-‘a’];
2)i++,继续取str[i],循环1中的操作,直到遇到结束符’\0’,此时将当前节点p中的exist置为true。

void insert(char *tar){    Trie* head = &Root;    while(*tar)    {        if(head->next[*tar-'a']==NULL)            head->next[*tar-'a'] = new Trie();        head = head->next[*tar-'a'];        tar++;    }    head->isWord = true;}

2、查找
假设要查找的字符串为str,Trie树的根节点为root。i=0,p=root。
1)取str[i],判断p->next[str[i]-97]是否为空,若为空,则返回false,若不为空,则p=p->next[str[i]-97],继续取字符。
2)重复1中的操作直到遇到结束符’\0’,若当前节点p不为空,并且exist为true,则返回true,否则返回false。

bool search(char *tar){    Trie* head = &Root;    while(*tar)    {        if(head->next[*tar-'a']==NULL)            return false;        head = head->next[*tar-'a'];        tar++;    }    if (head->isWord)        return true;    else        return false;}

3、删除
删除可以以递归的形式删除,先从儿子节点开始,一步步删除至根节点。
1)对于每个节点,循环从0到i-1,判断p->next[i]是否为空,若p->next[i]为空,继续访问该节点的孩子节点。
2)若该节点没有孩子节点,则删除该节点。

0 0
原创粉丝点击