一、概念

Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。
与二叉查找树不同,Trie树的键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。时间复杂度为O(m)。

 

如果在hole、honor、ahead、allow、all、always这几个单词中,查找always这个单词,需要首先把字母a从头到位和每个单词比较,比较成功再比较字母l,尔后比较字母w......

如果有n个单词,查找的单词长度为m,则这种查询方式的时间复杂度为O(nm)

字典树搜索java 字典树查询时间复杂度_字典树搜索java

 

上图以单词allow、all、always、ahead、hole、honor为例,建立的字典树,根节点为空,在每个结尾的字母的节点上做上标记。

二、结构体

 

字典树搜索java 字典树查询时间复杂度_子节点_02

 

 

在节点中,val存放字母,sign做标记,son是一个二级指针,指向一个node指针类型的数组,数组中的指针指向node节点(子节点)。

三、代码

/*
用结构体实现字典树
*/
#include <iostream>
#include <string>
using namespace std;
const int N = 26;		//

struct trieNode{	//字典树节点的结构体
	char val;		//节点值
	trieNode** son;		//二级指针
	int cnt;		//用于标记
	trieNode(char c) {		//初始化
		val = c;
		son = new trieNode*[N];
		for (int i = 0; i < N; i++) son[i] = 0;
		cnt = 0;
	}
};

trieNode * root;		//创建根节点

void insert(string s)		//插入方法,参数为输入的字符串
{
	trieNode* p = root;		//创建结构体指针p,指向root
	for (int i = 0; i < s.length(); i++) {	
		int c = s[i] - 'a';
		if (!p->son[c])		//假如节点的son指向的trieNode类型的表中的第c个元素为空
			p->son[c]= new trieNode(c);	//在节点的son指向的trieNode类型的表中的第c个元素创建一个新节点,c作为新节点的初始化参数
		p = p->son[c];		//指针p移动指向新节点
	}
	p->cnt++;	//改标记值,表示有单词
}
int query(string s)		//查询方法
{
	trieNode* p = root;		//trieNode类型的指针p
	for (int i = 0; i < s.length(); i++) {
		int c = s[i] - 'a';		//c作为查找的线索
		if (!p->son[c])return 0;	//如果指向的为空则返回0;
		p = p->son[c];		//如果存在,则指向下一个节点
	}
	return p->cnt;		//返回标记值;
}
/*bool delete_trie(string s)
{
	trieNode* p = root;
	int i = 0;
	if(delete_node(root,i,s))	return 1;	//删除完毕返回真
	return 0;			//否则返回假
}*/
bool delete_node(trieNode* p,int i,string s)	//删除单词,p指针指向节点,s是要删除的字符串,i是字符串中的字符序号
{
	if (i == s.length())	//边界条件
	{
		if (p->cnt > 0)		//如果标识大于0,则令标识为0,从标识符上来看表示已经删除该节点
			p->cnt=0;
		return 1;		//到达边界 返回真
	}
	int j = s[i] - 'a';		//j是子节点数组中的下标
	i++;				//i向后移
	if (!delete_node(p->son[j], i, s))		//递归,如果返回值为假就执行
		return 0;
	int sum = 0;	//sum用来计数
	for (int a = 0; a < N; a++)		//查看p指针指向的节点是否有多个子节点
	{
		if (p->son[a])	sum++;
	}
	if (sum >1||p->son[j]->son)		//如果有多个子节点,则不能删除,或者子节点后面还有子节点,也不能删除
		return 0;		
	delete p->son[j];		//释放子节点内存
	p->son[j] = nullptr;	//指针指向空
	return 1;		//删除成功
}
int main()
{
	root = new trieNode(' ');	//root节点初始化;
	int in = 10;
	string s;
	while (in) {
		cout << "请输入数字进行操作:1.插入;2.查询;3.删除;4.退出" << endl;
		cin >> in;
		switch (in)
		{
		case 1: {
			cout << "请输入:" << endl;
			cin >> s;
			insert(s);
			cout << s << "插入成功" << endl;
			break; }
		case 2: {
			cout << "请输入:" << endl;
			cin >> s;
			if (query(s))
				cout << s << "在字典树中查询到" << endl;
			else
				cout << s << "未查找到" << endl;
			break;
		}
		case 3: {
			cout << "请输入:" << endl;
			cin >> s;
			delete_node(root, 0, s);
				cout << s << "删除成功" << endl;
			break;
		}
		case 4:return 0;
		default: {
			cout << "输入错误!请重新输入" << endl;
			}
			break;
		}
	}
	return 0;
}

/*
用数组实现字典树
*/
#include <iostream>
#include<string>
using namespace std;
const int N = 26;		//对应26个英文字母
const int M = 105;		//输入单词最长长度

int trie[M][N] = { 0 };		//节点数组
int cnt[M] = { 0 };			//统计该节点有多少个单词
int idx = 0;			//标识

void insert(string s)		//插入函数
{
	int p = 0;		//p=0代表进入了根节点
	for (int i = 0; i < s.length(); i++) {
		int c = s[i] - 'a';
		if (!trie[p][c]) trie[p][c] = ++idx;		//如果节点为空,加入数字
		p = trie[p][c];			//p移到数组的下一行
	}
	cnt[p]++;		//以该字母为结束的单词数加一
}
int query(string s)		//查询函数
{
	int p = 0;		//从根节点进入
	for (int i = 0; i < s.length(); i++) {
		int c = s[i] - 'a';
		if (!trie[p][c])return 0;		//如果该节点为空,返回0
		p = trie[p][c];		//如果该节点不为空,则向下移
	}
	return cnt[p];		//返回标识
}
int main()
{
	int in = 10;
	string s;
	while (in) {
		cout << "请输入数字进行操作:1.插入;2.查询;3.退出" << endl;
		cin >> in;
		switch (in)
		{
		case 1: {
			cout << "请输入:" << endl;
			cin >> s;
			insert(s);
			cout << s << "插入成功" << endl;
			break; }
		case 2: {
			cout << "请输入:" << endl;
			cin >> s;
			if (query(s))
				cout << s << "在字典树中查询到" << endl;
			else
				cout << s << "未查找到" << endl;
			break;
		}
		case 3: {
			return 0;
		}
		default: {
			cout << "输入错误!请重新输入" << endl;
		}
				 break;
		}
	}
	return 0;
}

  运行结果:

字典树搜索java 字典树查询时间复杂度_ci_03