前缀树(字典树)Trie Tree

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。前缀树(字典树)Trie Tree,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

前缀树(字典树)Trie Tree

本文转载于https://blog.csdn.net/Jane_96/article/details/95900447

前缀树

含义

Trie树,即前缀树,又称单词查找树或字典树,是一种树形结构。该树典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

应用

题目:给你100000个长度不超过10的单词。对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位置。
在Trie树适用的状态下,很多情况同样可以使用hash树。但是Trie树用途更加的广泛,比如给一个字符串,要寻找有多少单词是以该字符串为前缀的问题。

代码解析

Trie树节点结构
const int branchNum = 26; // 26叉树
struct TrieNode {
	bool isStr; // 标识是否是一个完整的字符串
	TrieNode* next[branchNum]; // 每个字符的Trie树分支可能存在26个分支
};
Trie树插入操作
/*前缀树建立的过程*/
void Insert(TrieNode* root,const char* word)
{
	TrieNode* location=root;
	while(*word)
	{
		//若节点的子节点为空,创建一个子节点
		if(location->next[*word-'a']==NULL)
		{
			TrieNode* newNode=new TrieNode();
			newNode->isStr=false;
			memset(newNode->next,NULL,sizeof(newNode->next) );
			location->next[*word-'a']=newNode;
		}
		location=location->next[*word-'a']; //location指向字符串在前缀树中的位置
		word++; //当前字符在字符串中的位置
	}
	//字符串已经全部添加到前缀树中,
	//标识前缀树到该节点为止为完整字符串
	location->isStr=true;
}	
Trie树查找操作
/**
 * 遍历前缀树,查看指定字符串是否存在
 */
bool Search(TriNode* root , const char* word){
 	TrieNode* location = root;
 	while(*word && location != NULL){
  		location = location->next[*word - 'a'];
  		word++;
 	}
 	return (location != NULL && location->isStr);
}
Trie树删除操作
/**
 * 删除前缀树
 */
void Delete(TrieNode* location){	
	for(int i = 0; i < branchNum; i++){
  		if(location->next[i] != NULL){
   			Delete(location->next[i]);
  		}
 	}
 	delete location;
}
测试
#include<iostream>
#include<cstring>
using namespace std;
const int BRANCHNUM = 26;
struct TrieNode {
	TrieNode *next[BRANCHNUM];
	bool isStr;
}

void Insert(TrieNode *root , const char *word){	
	TrieNode *location = root;
 	while(*word){
  		if(location->next[*word - 'a'] == NULL){
   			TrieNode *newNode = new TrieNode();
   			newNode->isStr = false;
   			memset(newNode->next , NULL , sizeof(newNode->next));
   			location->next[*word - 'a'] = newNode;
  		}
  		location = location->next[*word - 'a'];
  		word++;
	 }
 	location->isStr = true; 
}

bool Search(TrieNode *root , const char *word){
	TrieNode *location = root;
	while(*word && location != NULL){
		location = location->next[*word - 'a'];
		word++;
	}
	return (location && location->isStr);
}

void Delete(TrieNode *root){
	for(int i = 0;i < BRANCHNUM;i++){
		if(root->next[i]){
			Delete(root->next[i]);
		}
	}	
	delete root;
}

int main(){
	TrieNode *root = new TrieNode();
	root->isStr = false;
	memset(root->next , NULL , sizeof(root->next)); 
	Insert(root , "apple");
	Insert(root , "banana");
	Insert(root , "car");
	cout << "是否包含单词bana : " << Search(root , "bana") << endl;
	cout << "是否包含单词car : " << Search(root , "car") << endl;
	
	return 0;
}

拓展

上述 Trie树结构中,每个node节点只含有两部分信息————节点是否是字符串的终点和节点的子节点。现在对节点添加两个其他信息————走过该节点的单词数(pathNum)和以该节点结尾的单词数(endNum);
作用:查询字符串集中某个字符串出现的次数或者以某个字符串为前缀的单词数

节点结构
const int branchNum = 26; // 26叉树
struct TrieNode {
	bool isStr; // 标识是否是一个完整的字符串
	TrieNode *next[branchNum]; // 每个字符的Trie树分支可能存在26个分支
    	int pathNum; // 走过该节点的单词数 , 即以所给字符串为前缀的单词数
    	int endNum; // 以该节点结尾的单词数 , 找相同单词的个数  
};
代码
#include<iostream>
#include<cstring>
using namespace std;

const int BRANCHNUM = 26;

struct TrieNode {
	bool isStr; // 标识是否是一个完整的字符串
	TrieNode *next[BRANCHNUM]; // 每个字符的Trie树分支可能存在26个分支
    int pathNum; // 走过该节点的单词数 , 即以所给字符串为前缀的单词数
    int endNum; // 以该节点结尾的单词数 , 找相同单词的个数  
};

void Insert(TrieNode *root , const char *word){
	TrieNode *location = root;
	while(*word){
		if(location->next[*word - 'a'] == NULL){
			TrieNode *newNode = new TrieNode();
			newNode->isStr = false;
			newNode->pathNum = 1;
			newNode->endNum = 0;
			memset(newNode->next , NULL , sizeof(newNode->next));
			location->next[*word - 'a'] = newNode;
		}else{
			location->next[*word - 'a']->pathNum = location->next[*word - 'a']->pathNum + 1; 
		} 
		
		location = location->next[*word - 'a'];
		
		word++;
	}
	location->isStr = true; 
	location->endNum += 1;
}

bool Search(TrieNode *root , const char *word){
	TrieNode *location = root;
	while(*word && location != NULL){
		location = location->next[*word - 'a'];
		word++;
	}
	return (location && location->isStr);
}

int SearchPathNum(TrieNode *root , const char *word){
	TrieNode *location = root;
	while(*word && location != NULL){ 
		location = location->next[*word - 'a'];
		word++;
		
	}
	if(location){
 		return location->pathNum;
 	}
 	return 0; 
}

int SearchEndNum(TrieNode *root , const char *word){
	TrieNode *location = root;
	while(*word && location != NULL){
		location = location->next[*word - 'a'];
		word++;
	}
	if(location){
 		return location->endNum;
 	}
 	return 0; 
}

void Delete(TrieNode *root){
	for(int i = 0;i < BRANCHNUM;i++){
		if(root->next[i]){
			Delete(root->next[i]);
		}
	}	
	delete root;
}

int main(){
	TrieNode *root = new TrieNode();
	root->isStr = false;
	root->pathNum = 1;
	root->endNum = 0;
	memset(root->next , NULL , sizeof(root->next)); 
	
	Insert(root , "apple");
	Insert(root , "ban");
	Insert(root , "bana");
	Insert(root , "banan");
	Insert(root , "banana");
	Insert(root , "car");
	Insert(root , "apple");
	Insert(root , "apple");
	
	cout << "以字符串ba为前缀的单词数有 : " << SearchPathNum(root , "ba") << "个" << endl;
	cout << "字符集中apple单词出现过 : " << SearchEndNum(root , "apple") << "次" << endl;
	
	return 0;
} 


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153852.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!