1. Trie树
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
1.根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2.从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3.每个节点的所有子节点包含的字符都不相同。
2.Trie树的实现
Trie树常常用来进行字符串匹配,前缀匹配,它主要实现以下四个函数
- void insert)String word):添加一个word,可重复添加
- void delete(String word):删除word,如果word添加过多次,仅删除一个
- boolean search(String word):查询word是否在字典树中
- int prefixNumber(String pre):返回以字符串pre为前缀的单词数量
字典树的结点可以这么设
public class TrieNode{
public int path;
public int end;
public TrieNode[] map;
public TrieNode(){
path = 0;
end = 0;
map = new TrieNode[26];
}
其中几个变量所代表的意义分别是:
path:代表有经过这个结点的单词数量
end:代表以这个结点结尾的单词数量
map:是一个哈希结构,key代表某个结点的值,字母,数字等等,value是相应的结点对象,在对单词构建字典树的时候,可以用一个26长度的数组表示
下面以单词的查找建立字典树为例,分别讲一下每个方法的具体实现。
1. void insert(String word)
算法思路:遍历单词每个字符,从根节点往下找,如果结点存在,则该结点path++,若不存在,增加一个结点,path++,遍历完后,最后一个结点end++。
public void insert(String word){
if(word == null)
return;
char[] ch = word.toCharArray();
TrieNode node = root;
int index = 0;
for(int i = 0; i < ch.length; i++){
index = ch[i] - 'a';
if(node.map[index] == null)
node.map[index] = new TrieNode();
node = node.map[index];
node.path++;
}
node.end++;
}
2.void search(String word)
算法思路:遍历单词每个字符,若发现某结点不存在,直接返回false。遍历完后,检验最后一个节点的end值,若end==0,没有单词以这个字符结尾,返回false,若end!=0,说明有单词以这个结尾,返回true。
public boolean search(String word){
if(word == null)
return false;
char[] ch = word.toCharArray();
TrieNode node = root;
int index = 0;
for(int i = 0; i < ch.length; i++){
index = ch[i] - 'a';
if(node.map[index] == null)
return false;
node = node.map[index];
}
if(node.end == 0)
return false;
return true;
}
3.void delete(String word)
算法思路:先用search检查单词是否存在,若不存在,直接返回,若存在,遍历单词,经过的每个结点path–,若path为1,则将后面的结点截断(设为null),最后一个结点end–。
public void delete(String word){
if(!search(word))
return;
char[] ch = word.toCharArray();
TrieNode node = root;
int index = 0;
for(int i = 0; i < ch.length; i++){
index = ch[i] - 'a';
if(node.path-- == 1) {
node.map[index] = null;
return;
}
node = node.map[index];
}
node.end--;
}
4.int prefixNumber(String pre)
算法思路:和search类似,找到最后一个字符的时候,返回path的值,若未找到结点,返回0。
public int prefixNumber(String pre){
if(pre == null)
return 0;
char[] ch = pre.toCharArray();
int index = 0;
TrieNode node = root;
for(int i = 0; i < ch.length; i++){
index = ch[i] - 'a';
if(node.map[index] == null)
return 0;
node = node.map[index];
}
return node.path;
}
3.Trie树的应用
1、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
end变量的值就代表了词频了,构建Trie的时候用一个最小堆维护top100的词汇,字典树是比用hashmap存储词频更节省空间的,因为字典树的结点会复用
2、1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
构建Trie树,search可以找到字符串是否存在,删掉重复的就可以了
3、 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
同第一题
4、寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
思路同第一题,有空再补重点内容
参考博客:http://blog.csdn.net/jiutianhe/article/details/8076835