目录
什么是T树(字典树)
字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统 计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
T树的定义
Trie树是一颗度m>=2的树,它的每一层分支不是靠整个关键码的值来确定,而是由关键码的一个分量来确定。
如图所示键树中,从结点Z到结点$为单支树,则在图相应的Trie树中只有一个含有关键字ZHAO及相关信息的叶子结点。由此,在Trie树中有两种结点:分支结点(含有d个指针域和一个指示该结点中非空指针域的个数的整数域)和叶子结点(含有关键字域和指向记录的指针域)。在分支结点中不设数据域,每个分支结点所表示的字符均由其双亲结点中(指向该结点)的指针所在位置决定。
构建一个T树
构建树的节点
由上图可以看出一颗T树由两大部分组成,即分支和元素(存取的值);
(1)分支表示
分支(Brch)是由一个数组组成,数组的每一个位存放的是树的指针,第一位位空(存放是该分支的共同元素);设计分支如下:
typedef struct
{
TrieNode* link[LETLEN];
}BrchType;
(2)元素表示
从图中可以看出,我们存放的每一个元素都有自己的大小和值组成;所以设计元素如下:
typedef struct
{
char ch[MaxKeySize];
int curentSize;
}KeyType;
typedef struct
{
KeyType key;
Record * recptr;
}ElemType;
(3)T树节点的表示:
因为图中可以看出每次层要么是分支要么是元素;为了节省空间可以设计为联合体;
struct TrieNode
{
NodeType utype; // ELEM , BRCH
union
{
ElemType item;
BrchType brch;
};
};
总体关系设计
const int MaxKeySize = 25;
const int LETLEN = 27;
typedef enum { BRCH = 0, ELEM = 1 } NodeType;
typedef struct
{
char ch[MaxKeySize];//分支,大小26个字母,0位置有其他含义
int curentSize;//单词的长度
}KeyType;
typedef struct{ }Record;//记录集
typedef struct
{
TrieNode* link[LETLEN];
}BrchType;
typedef struct
{
KeyType key;//关键码
Record* recptr;//记录集指针
}ElemType;
struct TrieNode
{
NodeType utype;
union
{
ElemType item;
BrchType brch;
};
};
T树的查找
在T树上进行查找的过程为:从根结点出发,沿和给定值相应的指针逐层向下,直至叶子结点,若叶子结点中的关键字和给定值相等,则查找成功,若分支结点中和给定值相应的指针为空,或叶结点中的关键字和给定值不相等,则查找不成功。
代码
TrieNode* root;
static int Index(const KeyType& kx, int k)
{
int index = 0;
if (k < kx.curentSize)
{
index = kx.ch[k] - 'a' + 1;
}
return index;
}
TrieNode* Find(const KeyType& kx)
{
TrieNode* p = root;
int k=0;
while (p != nullptr && p->utype == BRCH)
{
int index = Index(kx, k);
k += 1;
p = p->brch.link[index];
}
if (p != nullptr && strcmp(kx.ch, p->item.key.ch) != 0)
{
p = nullptr;
}
return p;
}
T树的插入
T树的插入也是非常容易理解,大致分为这几个步骤,以插入这几个单词为例{“cha”,"baby","chain","long"};
(1)先对传入的根节点判断是否为空,如果为空则建立根元素节点,直接连接根节点,也就是插入第一个单词“cha”
(2)如果根节点不为空,开始查询对应位置,如果是元素则改为分支,再连接;
(3) 如果根节点不为空,开始查询对应位置,如果是分支则连接;
代码
static TrieNode* Buynode()
{
TrieNode* s = (TrieNode*)malloc(sizeof(TrieNode));
if (nullptr == s)exit(1);
memset(s, 0, sizeof(TrieNode));
return s;
}
static TrieNode* MakeElem(const ElemType& item)
{
TrieNode* s = Buynode();
s->utype = ELEM;
s->item = item;
return s;
}
static TrieNode* MakeBrch(TrieNode* ptr, int k)
{
TrieNode* s = Buynode();
s->utype = BRCH;
int index = Index(ptr->item.key, k);
s->brch.link[index] = ptr;
return s;
}
void Insert_Item(TrieNode*& ptr, const ElemType& item, int k)
{
if (ptr == nullptr)
{
ptr = MakeElem(item);
}
else if (ptr->utype == BRCH)
{
int index = Index(item.key, k);
Insert_Item(ptr->brch.link[index], item, k + 1);
}
else if (ptr->utype == ELEM)
{
ptr = MakeBrch(ptr, k);
int index = Index(item.key, k);
Insert_Item(ptr->brch.link[index], item, k + 1);
}
}
T树删除
可以给分支域中加一个计数值,当插入新元素时就使其加1,反之等于0时将其分支删除,但是没多大意义;