338-T树(字典树)

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树只能插入不能删除

会使得整个树形结构产生大抖动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值