Trie树实现词典查找算法

        1.Trie树简介

        Trie树又称单词查找树,是一一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。

       
2.基本性质

        它有3个基本性质:

        (1)根节点不包含字符,除根节点外每一个节点都只包含一个字符;

        (2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;

        (3)每个节点的所有子节点包含的字符都不相同。

       
3.基本操作

        其基本操作有:查找、插入和删除,当然删除操作比较少见。查找Trie的方法为:

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

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

        (3)在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。

        (4)迭代过程……

        (5)在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。

        其他操作类似。

       
4.时间复杂度分析

        (1)建立词典的过程。需要将所有的单词加入到字典树中,假设单词的平均长度为m,词典规模为n个词,那么建立词典耗费的时间为O(m*n),由于该过程可能涉及文件IO,因此速度可能较慢。

        (2)查询过程。查询过程只需要按照指定的字符串逐级向下搜索即可,时间复杂度为O(m)。

        (3)删除过程。同查询过程类似,时间复杂度为O(m)。

        下图为对10w个字符串进行建立Trie树和搜索某个字符串的结果,该过程查询操作仅用了不到1ms。同快速排序+二分法查找法相比(查找耗时20ms),性能大大提高。 

               
《Trie树实现词典查找算法》

        5.源代码

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>	//函数srand()
#include <ctime>	//函数time()
#include <vector>
#include <sys/time.h>
#include <algorithm>
#include <cstring>

using namespace std;

const long LEN = 100000;	//词典的规模
const int MAX = 30;		//词条的最大长度
const int MIN = 10;		//词条的最小长度
int limit_value = 25;		//进行插入排序的阈值

const int Son_Num = 26;
//---------------------Trie树的定义----------------------------
//节点定义
class Node{
	public:
		int flag;		//节点的是否有单词出现的标记
		int count;		//单词出现的次数
		Node* son[Son_Num];	//指向分支节点的指针,这里为26个
	public:
		Node() : flag(0),count(0){
			memset(son, NULL, sizeof(Node*) * Son_Num);
		}
};

//trie树的操作定义
class Trie{
	private:
		Node* root;
	public:
		Trie();
		~Trie();
		void insert(string str);
		bool search(string str, int &count);
		bool remove(string str);
		void destory(Node* root);
};

//构造函数
Trie::Trie(){
	root = new Node();
}

//析构函数
Trie::~Trie(){
	destory(root);
}

void Trie::insert(string str){
	int index = 0;
	Node* pNode = root;
	//不断向下搜索单词的字符是否出现
	for(int i = 0; i < str.size(); i++){
		index = str[i] - 'a';
		//字符在规定的范围内时,才进行检查
		if(index >= 0 && index < Son_Num){
			//父节点是否有指针指向本字符
			if(NULL == pNode -> son[index]){
				pNode -> son[index] = new Node();
			}
			//指针向下传递
			pNode = pNode -> son[index];
		}else{
			return;
		}
	}
	//判断单词是否出现过
	if(pNode -> flag == 1){
		//单词已经出现过,计数器加1
		pNode -> count++;
		return;
	}else{
		//单词没有出现过,标记为出现,计数器加1
		pNode -> flag = 1;
		pNode -> count++;
	}
}

//搜索指定单词,并返回单词出现次数(如果存在)
bool Trie::search(string str, int &count){
	Node* pNode = root;
	int index = 0;
	int i = 0;
	while(pNode != NULL && i < str.size()){
		index = str[i] - 'a';
		if(index >= 0 && index < Son_Num){
			//字符在指定的范围内时
			pNode = pNode -> son[index];
			i++;
		}else{
			//字符不再指定的范围内
			return false;
		}
	}
	//判断字符串是否出现过
	if(pNode != NULL && pNode -> flag == 1){
		count = pNode -> count;
		return true;
	}else{
		return false;
	} 
}

//删除指定的单词
bool Trie::remove(string str){
	Node* pNode = root;
	int i = 0;
	int index = 0;
	while(pNode != NULL && i < str.size()){
		index = str[i] - 'a';
		if(index >= 0 && index < Son_Num){
			pNode = pNode -> son[index];
			i++;
		}else{
			return false;
		}
	}
	if(pNode != NULL && pNode -> flag == 1){
		pNode -> flag = 0;
		pNode -> count--;
		return true;
	}else{
		return false;
	}
}

//销毁Trie树
void Trie::destory(Node* root){
	if(NULL == root){
		return;
	}
	for(int i = 0; i < Son_Num; i++){
		destory(root -> son[i]);
	}
	delete root;
	root == NULL;
}

//-------------------------------------------------------------

//生成随机字符串
string rand_string(int min, int max){
	char a[MAX+1];
	int len = rand()%(max - min) + min;
	for(int i = 0; i < len; i++){
		a[i] = rand()%26 + 'a';
	}
	a[len] = '\0';
	string str(a);
	return str;
}

//将字符串写入文件,构建词典
void write_dict(){
	ofstream fout;
	fout.open("dict.txt");
	srand((unsigned)time(NULL));
	for(int i = 0; i < LEN; i++){
		string str = rand_string(10, 30); 
		fout << str << '\n';
		fout << flush;
	}
	fout.close();
}

//从文件中读取词典信息
vector<string> read_dict(){
	ifstream fin;
	fin.open("dict.txt");
	string textline;
	vector<string> dict;
	while(getline(fin, textline, '\n')){
		dict.push_back(textline);
	}
	return dict;
}



//-----------------测试代码---------------------------

long getCurrentTime(){
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return tv.tv_sec*1000*1000 + tv.tv_usec;
}


int main(){
	
	Trie trie;
	string str;
	ifstream fin;
	fin.open("dict.txt");

	//建立Trie树
	long time_1 = getCurrentTime();
	while(getline(fin, str, '\n')){
		trie.insert(str);
	}
	long time_2 = getCurrentTime();
	fin.close();
	
	//查找
	str = "siubetamwm";
	int count = -1;
	long time_3 = getCurrentTime();
	bool isFind = trie.search(str, count);
	cout << "要查找的字符串:" << str << endl; 
	long time_4 = getCurrentTime();
	if(isFind){
		cout << "  该单词存在,存在次数:" << count << endl;
	}else{
		cout << "  该单词不存在!" << endl;
	}

	//删除
	str = "lutgjrxjavgfkij";
	cout << "要删除的字符串:" << str << endl; 
	bool isRemove = trie.remove(str);	
	if(isRemove){
		cout << "  该单词存在,删除成功!" << endl;
	}else{
		cout << "  该单词不存在,删除失败!" << endl;
	}
	
	cout << "建立Trie树用时(纳秒):" << time_2 - time_1 << endl;
	cout << "查找字符串用时(纳秒):" << time_4 - time_3 << endl;


}

    原文作者:Trie树
    原文地址: https://blog.csdn.net/u010189459/article/details/30516087
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞