Trie树标准模版

Posted FFjet

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Trie树标准模版相关的知识,希望对你有一定的参考价值。

这是一个Trie树标准模版

By Leo

本人版权,请勿抄袭!!

先看教程:

 1. 什么是trie树

  1.Trie树 (特例结构树)  

      Trie树,又称单词查找树、字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
     Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
     Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.

2.  三个基本特性:  

1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。  
2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 
3)每个节点的所有子节点包含的字符都不相同。

3 .例子

       和二叉查找树不同,在trie树中,每个结点上并非存储一个元素。
       trie树把要查找的关键词看作一个字符序列。并根据构成关键词字符的先后顺序构造用于检索的树结构。
       在trie树上进行检索类似于查阅英语词典。
      一棵m度的trie树或者为空,或者由m棵m度的trie树构成。

     例如,电子英文词典,为了方便用户快速检索英语单词,可以建立一棵trie树。例如词典由下面的单词成:a、b、c、aa、ab、ac、ba、ca、aba、abc、baa、bab、bac、cab、abba、baba、caba、abaca、caaba

技术分享

 

           再举一个例子。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:

        技术分享

 

        可以看出:

  • 每条边对应一个字母。
  • 每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
  • 单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀"a",所以他们共享从根节点到节点"a"的边。

查询操纵非常简单。比如要查找int,顺着路径i -> in -> int就找到了。

 2. trie树的实现

1.插入过程

对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支向下走,直到单词遍历完,将最后的节点标记为红色,表示该单词已插入trie树。

2. 查找过程

其方法为:

(1) 从根结点开始一次搜索;

(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;

(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……
(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。其他操作类似处理.

       即从根开始按照单词的字母顺序向下遍历trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未标记为红色,则表示该单词不存在,若最后的节点标记为红色,表示该单词存在。如下图中:trie树中存在的就是abc、d、da、dda四个单词。在实际的问题中可以将标记颜色的标志位改为数量count等其他符合题目要求的变量。  

      技术分享

 

代码上有英文注释,请还是不懂的人看一看。。

再看不懂,本人也无能为力了~~~

代码:

/**
  *			  Leolee 2017(C)
  *				Trie.cpp
  * This is a standard Trie‘s template by Leolee
  * The program has its copyright and you cannot copy it
  * before the author allowed.
*/

//Define the TRIE Template here
#ifndef _TRIE_
#define _TRIE_

#include <iostream>    //The standard IO stream
#include <sstream>      //The standard String library and string stream
#include <cstring>	//The C-style string library
#include <cassert>	//The C-style assert library
#include <climits>	//For using "INT_MIN"

#define MAXNUM 26	//The max num of the children of a node
using namespace std;	//using the standard namespace "std"

//define the Trie node here
struct Trie_Node {
	string word;//The word
	int count;	//The number of occurrences of a word
	Trie_Node *Next_Branch[MAXNUM];//Pointer to a 26-character node
	Trie_Node() : count(0)
	{
		word.empty();
		memset(Next_Branch, NULL, sizeof(Trie_Node*) * MAXNUM);
	}
	~Trie_Node() {};
};

class Trie {
private:
	Trie_Node* ROOT;
private:
	void Reset(Trie_Node* Root);		//Reset The trie tree
	void Print(Trie_Node* Root);
public:
	void Insert(string str);			//Insert the string str
	bool Search(string str, int& cnt);	//Find the string str and return the number of occurrences
	bool Remove(string str);			//Delete the string str
	void PrintALL();					//Prints all the nodes in the trie tree
	bool PrintPre(string str);			//Print a word prefixed with str
	Trie() {
		ROOT = new Trie_Node();			//Note that the root of the dictionary tree does not hold characters
	};
	~Trie() {
		Reset(ROOT);
	}; 
};

#endif //_TRIE_

//Insert a word
void Trie::Insert(string str)
{
	if (str.empty()) return;
	Trie_Node *NODE = ROOT;
	int len = str.size();
	for (int i = 0;i < len;i++)
	{
		int index = str.at(i) - ‘a‘;	//Case sensitive
		if (index < 0 || index > MAXNUM)//No insertion is performed
			return;
		if (NODE->Next_Branch[index] == NULL)			//The prefix of the word does not exist and is to be generated for that node
			NODE->Next_Branch[index] = new Trie_Node();
		NODE = NODE->Next_Branch[index];   				//Go to the next node 
	}
	if (!NODE->word.empty())							//The word has already appeared
	{
		NODE->count++;
		return;
	}
	else
	//The word did not appear, and we should insert it
	{
		NODE->count++;
		NODE->word = str;
	}
}

//Find a word, if it appeared, then return the number of occurrences of the word.
//If not, it will return false
bool Trie::Search(string str, int& cnt)
{
	assert(!str.empty());
	int index = INT_MIN;
	Trie_Node *NODE = ROOT;
	int length = str.size();
	int i = 0;
	while (NODE && i < length)
	{
		index =  str.at(i) - ‘a‘;			//Case sensitive
		if (index < 0 || index > MAXNUM)	//No insertion is performed
			return false;
		NODE = NODE->Next_Branch[index];
		i++;
	}
	if (NODE && !NODE->word.empty())
	{
		cnt = NODE->count;
		return true;
	}
	return false;
}

bool Trie::Remove(string str)
{
	assert(!str.empty());
	int index = INT_MIN;
	Trie_Node *NODE = ROOT;
	int length = str.size();
	int i = 0;
	while (NODE && i < length)
	{
		index =  str.at(i) - ‘a‘;			//Case sensitive
		if (index < 0 || index > MAXNUM)	//No deletion is performed
			return false;
		NODE = NODE->Next_Branch[index];
		i++;
	}
	if (NODE && !NODE->word.empty())
	{
		NODE->word.clear();
		return true;
	}
}

void Trie::PrintALL()
{
	Print(ROOT);
}

bool Trie::PrintPre(string str)
{
	assert(!str.empty());
	int index = INT_MIN;
	Trie_Node *NODE = ROOT;
	int length = str.size();
	int i = 0;
	while (NODE && i < length)
	{
		index =  str.at(i) - ‘a‘;		//Case sensitive
		if (index < 0 || index > MAXNUM)
			return false;
		NODE = NODE->Next_Branch[index];
		i++;
	}
	if (NODE)	//We can find the word
	{
		Print(NODE);
		return true;
	}
	return false;
}

void Trie::Print(Trie_Node* Root)
{
	if (Root == NULL)
		return;
	//Print the word
	if (!Root->word.empty())
		cout << Root->word << " " << Root->count << endl;
	
	for (int i = 0;i < MAXNUM;i++)
		Print(Root->Next_Branch[i]);//Print all the children of the node
}

//Rest trie tree
void Trie::Reset(Trie_Node* Root)
{
	if (Root == NULL)
		return;
	for (int i = 0;i < MAXNUM;i++)
		Reset(Root->Next_Branch[i]);
	//Reset the word
	if (!Root->word.empty())
		Root->word.clear();
	delete Root;	//Delete the node
	Root = NULL;
}

int NUM_TO_INSERT, NUM_TO_SEARCH, NUM_TO_DELETE, NUM_TO_SBF;

//Main Function
int main(int argc, char **argv)
{
	ios::sync_with_stdio(false);
	Trie TREE;
	cout << "Input the number of words you want to insert:" << endl;
	cin >> NUM_TO_INSERT;
	
	for (int i = 0;i < NUM_TO_INSERT;i++)
	{
		string STRING;
		cin >> STRING;
		TREE.Insert(STRING);
	}
	
	cout << endl;
	cout << "Input the number of words you want to search:" << endl;
	cin >> NUM_TO_SEARCH;
	
	for (int i = 0;i < NUM_TO_SEARCH;i++)
	{
		string STRING;
		cin >> STRING;
		int count = -1;
		bool CanFind = TREE.Search(STRING, count);
		if (CanFind)
			cout << STRING << " exists, its number of occurrences is " << count << endl;
		else
			cout <<  STRING << " does not exists!" << endl;
	}
	cout << endl;
	cout << "Here are all the nodes in the Trie tree:" << endl;
	
	TREE.PrintALL();
	
	cout << endl;
	cout << "Input the number of words you want to delete:" << endl;
	cin >> NUM_TO_DELETE;
	for (int i = 0;i < NUM_TO_DELETE;i++)
	{
		string STRING;
		cin >> STRING;
		bool Is_Deleted = TREE.Remove(STRING);
		if (Is_Deleted)
			cout << STRING << " deleted!" << endl;
		else
			cout << "Failed to delete " << STRING << endl;
	}
	
	cout << endl;
	cout << "Input the number of words you want to search by prefix:" << endl;
	cin >> NUM_TO_SBF;
	for (int i = 0;i < NUM_TO_SBF;i++)
	{
		string STRING;
		cin >> STRING;
		bool STATUS = TREE.PrintPre(STRING);
		if (!STATUS)
			cout << "There aren‘t any words begin with " << STRING << " !";
		cout << endl;
	}
	cout << endl;
	return 0;
}

  



以上是关于Trie树标准模版的主要内容,如果未能解决你的问题,请参考以下文章

一个通用的Trie树,标准C++实现

字典树Trie学习二:Java实现方式之一

Trie模版

UVA 11488 Hyper Prefix Sets 字典树

HDU 1251 统计难题(Trie模版题)

数据结构—前缀树Trie的实现原理以及Java代码的实现