Trie树(字典树)
T树的结构
最上面一排是一个个的分枝结点(对应着0-26下标,英文字母是A-Z)分枝结点放着一个个指针,如果我们查询CHAO,或者是CHANG,查询的方式是:靠关键码的分量来确定,不超过关键码的长度。
举个例子:
如果我们要查询CHANG这个字符关键码,从根节点出发(最上层),这个关键码不是一个个的元素,我们取出关键码CHANG的第一个元素C,字符C减去字符A加上1(因为是从1下标开始),结果是3(即3下标),我们拿指针指向C下标(即3下标)的连接的分枝,(不是元素),我们拿CHANG的第二个元素H来找,跟字典一样嘛,拿指针指向H下标的分枝结点,也不是一个元素,我们拿CHANG的第三个元素A来找,拿指针指向A下标下的连接的分枝结点,不是一个元素,我们拿CHANG的第四个元素N来找,拿指针指向N下标对应的连接的分枝结点,是CHANG,是一个元素!我们把这个元素和字符串CHANG对比,就知道这两个字符是不是相同了。
就是按照关键码的单个分量来查询(字典)
整个T树分为两个结构:结点结构,元素结构(含有关键码,关键码对应的记录集K-VALUE)
建立T树的代码
#include<iostream>
using namespace std;
const int MaxKeySize = 25;//关键码(单词)的长度不超过25个
typedef struct
{
char ch[MaxKeySize+1];//存放字符串(单词),+1是因为有\0, "linzey"
int currentsize;//当前放的字符串的个数,6
}KeyType;
typedef struct{}Record;//记录集
typedef enum{BRCH,ELEM}NodeType;//分枝,元素
struct TrieNode
{
NodeType utype;//类型,分枝类型还是元素类型
union
{
struct
{
TrieNode* link[27];//分枝,27个分枝树,1-26指向字母。0特殊,在最前面。
}brch;
struct
{
KeyType key;//关键码
Record* recptr;//记录集指针
}elem;//元素
};
};
T树的查询函数
有一个root指针指向根节点。来判断这个结点是不是元素类型,如果是元素类型就和这个值比较,如果不是元素类型,就是分枝结点类型。去字符串的第一个字符,查找对应的下标,对应的下标的连接的结点。一直查询下去。如果是元素类型就停止查询,进行字符比较大小。
class TrieTree
{
TrieNode* root;//根节点
int StringPos(const KeyType& kx, int k)//获得字符串的位置信息
{
int pos = 0;
if (k < kx.currentsize)
{
pos = kx.ch[k] - 'a' + 1;
}
return pos;
}
public:
TrieTree():root(nullptr){}
~TrieTree(){}
bool Insert(const KeyType& kx, Record* recptr);
TrieNode* FoundValue(const KeyType& kx)//查询函数
{
TrieNode* p = root;//指向根节点
int k = 0;//从第0个字符查询
while (p != nullptr && p->utype == BRCH)//是分枝类型
{
int pos = StringPos(kx, k);
k++;//向下一层迈进
p = p->brch.link[pos];
}
if (p != nullptr&&strcmp(p->elem.key.ch,kx.ch)!=0)//是元素类型
{
p = nullptr;
}
return p;//找到了
}
};
T树的插入函数
依次插入 “data" 4 (空的,申请一个元素结点)
然后插入"eye" 3(申请一个分枝,挂接)
class TrieTree
{
TrieNode* root;//根节点
int StringPos(const KeyType& kx, int k)//获得字符串的位置信息
{
int pos = 0;
if (k < kx.currentsize)
{
pos = kx.ch[k] - 'a' + 1;
}
return pos;
}
TrieNode* Buynode()//购买结点
{
TrieNode* s = (TrieNode*)malloc(sizeof(TrieNode));
if (s == nullptr) exit(1);
memset(s, 0, sizeof(TrieNode));
return s;
}
TrieNode* BuyElem(const KeyType kx, Record* rec)//购买元素结点
{
TrieNode* s = Buynode();
s->utype = ELEM;
s->elem.key = kx;
s->elem.recptr = rec;
return s;
}
TrieNode* BuyBrch(TrieNode* ptr, int k)//购买分枝结点
{
TrieNode* s = Buynode();
s->utype = BRCH;
int pos = StringPos(ptr->elem.key, k);
s->brch.link[pos] = ptr;
return s;
}
void Insert_Item(TrieNode*& ptr, const KeyType kx, Record* rec, int k)//插入
{
if (ptr == nullptr)
{
ptr = BuyElem(kx, rec);//购买元素结点
}
else if (ptr->utype = BRCH)//等于分枝类型
{
int pos = StringPos(kx, k);
Insert_Item(ptr->brch.link[pos], kx, rec, k + 1);
}
else//等于元素类型
{
ptr = BuyBrch(ptr, k);
int pos = StringPos(kx, k);
Insert_Item(ptr->brch.link[pos], kx, rec, k + 1);
}
}
public:
TrieTree():root(nullptr){}
~TrieTree(){}
bool Insert(const KeyType& kx, Record* rec)//插入函数
{
TrieNode* p = FoundValue(kx);
if (p != nullptr)//有相同的关键码
{
return false;//插入失败
}
int k = 0;//一层一层往下走
Insert_Item(root, kx, rec, k);
return true;
}
TrieNode* FoundValue(const KeyType& kx)//查询函数
{
TrieNode* p = root;//指向根节点
int k = 0;//从第0个字符查询
while (p != nullptr && p->utype == BRCH)//是分枝类型
{
int pos = StringPos(kx, k);
k++;//向下一层迈进
p = p->brch.link[pos];
}
if (p != nullptr&&strcmp(p->elem.key.ch,kx.ch)!=0)//是元素类型
{
p = nullptr;
}
return p;//找到了
}
};
程序测试
测试插入
#include<iostream>
using namespace std;
const int MaxKeySize = 25;//关键码(单词)的长度不超过25个
typedef struct
{
char ch[MaxKeySize+1];//存放字符串(单词),+1是因为有\0, "linzey"
int currentsize;//当前放的字符串的个数,6
}KeyType;
typedef struct{}Record;//记录集
typedef enum{BRCH,ELEM}NodeType;//分枝,元素
struct TrieNode
{
NodeType utype;//类型,分枝类型还是元素类型
union
{
struct
{
TrieNode* link[27];//分枝,27个分枝树,1-26指向字母。0特殊,在最前面。
}brch;
struct
{
KeyType key;//关键码
Record* recptr;//记录集指针
}elem;//元素
};
};
class TrieTree
{
TrieNode* root;//根节点
int StringPos(const KeyType& kx, int k)//获得字符串的位置信息
{
int pos = 0;
if (k < kx.currentsize)
{
pos = kx.ch[k] - 'a' + 1;
}
return pos;
}
TrieNode* Buynode()//购买结点
{
TrieNode* s = (TrieNode*)malloc(sizeof(TrieNode));
if (s == nullptr) exit(1);
memset(s, 0, sizeof(TrieNode));
return s;
}
TrieNode* BuyElem(const KeyType kx, Record* rec)//购买元素结点
{
TrieNode* s = Buynode();
s->utype = ELEM;
s->elem.key = kx;
s->elem.recptr = rec;
return s;
}
TrieNode* BuyBrch(TrieNode* ptr, int k)//购买分枝结点
{
TrieNode* s = Buynode();
s->utype = BRCH;
int pos = StringPos(ptr->elem.key, k);
s->brch.link[pos] = ptr;
return s;
}
void Insert_Item(TrieNode*& ptr, const KeyType kx, Record* rec, int k)//插入
{
if (ptr == nullptr)
{
ptr = BuyElem(kx, rec);//购买元素结点
}
else if (ptr->utype = BRCH)//等于分枝类型
{
int pos = StringPos(kx, k);
Insert_Item(ptr->brch.link[pos], kx, rec, k + 1);
}
else//等于元素类型
{
ptr = BuyBrch(ptr, k);
int pos = StringPos(kx, k);
Insert_Item(ptr->brch.link[pos], kx, rec, k + 1);
}
}
public:
TrieTree():root(nullptr){}
~TrieTree(){}
bool Insert(const KeyType& kx, Record* rec)//插入函数
{
TrieNode* p = FoundValue(kx);
if (p != nullptr)//有相同的关键码
{
return false;//插入失败
}
int k = 0;//一层一层往下走
Insert_Item(root, kx, rec, k);
return true;
}
TrieNode* FoundValue(const KeyType& kx)//查询函数
{
TrieNode* p = root;//指向根节点
int k = 0;//从第0个字符查询
while (p != nullptr && p->utype == BRCH)//是分枝类型
{
int pos = StringPos(kx, k);
k++;//向下一层迈进
p = p->brch.link[pos];
}
if (p != nullptr&&strcmp(p->elem.key.ch,kx.ch)!=0)//是元素类型
{
p = nullptr;
}
return p;//找到了
}
};
int main()
{
KeyType ar[] = { "data",4,"eye",3,"date",4,"dat",3 };
int n = sizeof(ar) / sizeof(ar[0]);
TrieTree myt;
for (int i = 0; i < n; ++i)
{
myt.Insert(ar[i], nullptr);
}
return 0;
}
测试查询
T树只能插入不能删除
会使得整个树形结构产生大抖动。