字典树(Trie)归纳总结,模版

字典树,还是很好理解的,主要是每个人都想把它写的漂亮.顺手,那么自己就得多打,

多改进了,我给大家看看我的

#include 
#include 
#include 
#include 
/*
 *字典树---Trie
 */ 

using namespace std;

const int sta = 'a';//这是规定的起始位置 

const int maxn = 26;

int N;

int M;

struct Trie
{
	Trie *next[maxn];//当然最大值是根据你数据的规模来定的 
	int v;//标记重复访问此字母的次数 
	int flag;//标记是否是一个单词,1表示,到这个字母位置,是一个单词,2表示,到这个字母位置位置并不是一个完整的单词 
	Trie()//这是初始化,这样封装的目的是容易看,容易理解 
	{
		v = 1;//初始化次数为1 
		flag = 0;//初始化标记为不是完整的单词 
		memset(next, 0, sizeof(next));//将该字母后的所有尾巴标记为未访问 
	}
}*root;

void Init()
{
	root = new Trie();//记住一定要有这个初始化,也就是一定要在内存中开辟一个这样的单元,要不然就爆掉内存了.程序无法运行 
}

void creatTrie(char *str)//这个写法就比较官方了,这个大体大家的写法都相差无几 
{
	int len = strlen(str);
	Trie *p = root;
	Trie *q;
	for (int i = 0; i < len; i++)
	{
		int id = str[i] - sta;
		if (p->next[id] == 0)
		{
			q = new Trie();
			p->next[id] = q;
			p = q;
		}
		else
		{
			p->v++;
			p = p->next[id];
		}
	}
	p->flag = 1;
}

int searchTrie(char *pat)
{
	int len = strlen(pat);
	Trie *p = root;
	for (int i = 0; i < len; i++)
	{
		int id = pat[i] - sta;
		if (p->next[id] == 0)
		{
			return 0;
		}
		else
		{
			p = p->next[id];
		}
	}	
	if (p->flag == 1)//如果这个单词都访问完了,就可以判断这个单词是不是已经存储过,如果是的话当然flag应该是1; 
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

void freeTrie(Trie *p)//用来释放空间,防止爆掉内存,这个会不会把堆栈爆了???不清楚,,, 
{
	int i = 0;
	for (int i = 0; i <26; i++)
	{
		if (p->next[i] != 0)//如果该结点后面还有别的结点的话,就递归删除后面的结点,递归调用 
		{
			freeTrie(p->next[i]);
		}
	}
	free(p);
}


int main()
{
	while (scanf("%d", &N) != EOF)
	{
		char str[11];
		char pat[11];
		Init();//这个是很必须的 
		for (int i = 1; i <= N; i++)
		{
			scanf("%s", str);
			creatTrie(str);
		}
		scanf("%d", &M);
		for (int i = 1; i <= M; i++)
		{
			scanf("%s", pat);
			int ans = searchTrie(pat);
			if (ans == 1)
			{
				cout << "YES" << endl;
			}
			else
			{
				cout << "NO" << endl;
			}
		} 
//		delete root; // 如果对内存要求不是很高的话就每次把头结点删除一下就行了,这样的话,就不会出现上面存的
					//在下面也有了. 
		freeTrie(root);	
	}
	system("pause");
	return 0;
}


你可能感兴趣的:(字典树)