Trie树

Trie树:

http://hihocoder.com/problemset/problem/1014?sid=1526165

描述

小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?

身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字符串是不是这个单词的前缀不就是了?

小Hi笑道:“你啊,还是太年轻了!~假设这本词典里有10万个单词,我询问你一万次,你得要算到哪年哪月去?”

小Ho低头算了一算,看着那一堆堆的0,顿时感觉自己这辈子都要花在上面了...

小Hi看着小Ho的囧样,也是继续笑道:“让我来提高一下你的知识水平吧~你知道树这样一种数据结构么?”

小Ho想了想,说道:“知道~它是一种基础的数据结构,就像这里说的一样!”

小Hi满意的点了点头,说道:“那你知道我怎么样用一棵树来表示整个词典么?”

小Ho摇摇头表示自己不清楚。

提示一:Trie树的建立

“你看,我们现在得到了这样一棵树,那么你看,如果我给你一个字符串ap,你要怎么找到所有以ap开头的单词呢?”小Hi又开始考校小Ho。

“唔...一个个遍历所有的单词?”小Ho还是不忘自己最开始提出来的算法。

“笨!这棵树难道就白构建了!”小Hi教训完小Ho,继续道:“看好了!”

提示二:如何使用Trie树

提示三:在建立Trie树时同时进行统计!

“那么现在!赶紧去用代码实现吧!”小Hi如是说道

 

网上说这个字典树的太多了,不说了。

需要注意的地方

1. 如果当前字符不在节点(如root节点)的子节点列表里,新建节点插入作为 root节点子节点后,记得将root置为新建节点,后续插入是在以新节点为根节点的树上插入的。对应到代码中就是:

if ( j == root->childs.end() ) {//如果当前字符在子节点列表不存在,则新建一个节点作为当前父节点的子节点,并从该子树开始插入
	TrieNode* trieNode = new TrieNode( str[i] );
	trieNode->count++;
	root->childs.push_back(trieNode);
	root = trieNode;
}

 2.如果在for循环中将迭代器所使用的容器改变了,会出错的,如

list<TrieNode>::iterator j = root.childs.begin();
for ( ; j != root.childs.end(); ++j ) {
if ( j->character == str[i] ) {//如果当前字符在某个子节点存在
	root = *j;,
    break;
    
}
//error: list iterators incompatible
//出错,无法比较
if( j==root.childs.end() ){

}

3. 你也可以参考这篇文章编写代码,但需要注意的是,这里 

node *next[26];    //定义的字典树为只有26个小写字母,可增加减少

因为26个字母和数字一一对应,所以可以直接采用这种方式保存子节点,子节点字符就是索引对应的字符。

但是,如果题目说,这里不一定是26个字母,也可能是别的符号,那么这种方法就不好使用了。

4.搜索,插入,能不能使用递归?当然可以了,明显,子问题和大问题相同,只要改变root,还有字符串就行。

比如把abc,已经插入了a,那么下一次就是插入字符串bc。

// Trie树.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <list>
#include <map>
#include <string>

using namespace std;

class TrieNode
{
public:
	TrieNode( char character );
	~TrieNode();
	list<TrieNode*> childs;
	char character;
	void insert(TrieNode* trieNode, string str);
	TrieNode* find(TrieNode* trieNode, string str);
	int count;
private:

};

//子节点与保存的字符起始状态都为空
TrieNode::TrieNode( char character = NULL )
	:character(character),count(0)
{
}

TrieNode::~TrieNode()
{
}

void TrieNode::insert(TrieNode* root, string str)
{
	
	for (int i = 0; i < str.size(); ++i) {//依次插入字符串的每个字符
		if ( root->childs.size() == 0 ) {//父节点无子节点,直接新建节点插入
			TrieNode* trieNode = new TrieNode( str[i] );
			trieNode->count++;
			root->childs.push_back(trieNode);
			root = trieNode;
		}else {
			//遍历子节点列表,查看当前字符是否与某子节点字符相同,相同则沿着该子节点继续搜索
			list<TrieNode*>::iterator j = root->childs.begin();
			for ( ; j != root->childs.end(); ++j ) {
				if ( (*j)->character == str[i] ) {//如果当前字符在某个子节点存在
					break;
				}
			}
			if ( j == root->childs.end() ) {//如果当前字符在子节点列表不存在,则新建一个节点作为当前父节点的子节点,并从该子树开始插入
				TrieNode* trieNode = new TrieNode( str[i] );
				trieNode->count++;
				root->childs.push_back(trieNode);
				root = trieNode;
			}
			else {//如果当前字符在子节点列表里
				root = *j;
				(*j)->count++;
			}
		}
	}	
}

//查找字符串对应的叶子节点
TrieNode* TrieNode::find(TrieNode* root, string str)
{
	for (int i = 0; i < str.size(); ++i) {//依次查找字符串的每个字符
		if (root->childs.size() == 0) {//父节点无子节点
			return NULL;
		}
		else {
			//遍历子节点列表,查看当前字符是否与某子节点字符相同,相同则沿着该子节点继续搜索
			list<TrieNode*>::iterator j;
			for (j = root->childs.begin(); j != root->childs.end(); ++j) {
				if ( (*j)->character == str[i]) {//如果当前字符在某个子节点存在,在该子树上继续查找
					break;
				}
			}
			if (j == root->childs.end()) {//如果当前字符在子节点列表不存
				return NULL;
			}
			else {
				root = *j;
			}
		}
	}
	return root;

}

int main()
{
	int inputStringTimes = 0;
	cin >> inputStringTimes;

	TrieNode* trie = new TrieNode();
	list<string> wordList;

	while ( inputStringTimes>0 )
	{
		inputStringTimes--;
		string str;
		cin >> str;
		wordList.push_back(str);
		trie->insert(trie, str );
	}
	
	int checkTimes = 0;
	cin >> checkTimes;

	while (checkTimes > 0)
	{
		checkTimes--;
		string str;
		cin >> str;
		TrieNode* t = trie->find(trie, str);
		if ( NULL != t )
		{
			cout << t->count << endl;
		}
		else {
			cout << 0 << endl;
		}
	}
}

很久,估计两年左右没做过题了,纪念下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值