Trie Tree(Prefix Tree)

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

3个基本性质:

  1. 根节点不包含字符,除了根节点外每一个节点都只包含一个字符;
  2. 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串;
  3. 每个节点的所有子节点包含的字符都不相同。
                                                     
字典树的插入,查找只需要使用一次循环即可完成:
从根节点开始一次搜索,取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索,在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索,经过若干次迭代之后,在某个节点处,关键词的所有字母已被取出,则读取附在该节点上的信息,即完成查找。
  Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
  Trie树也有它的缺点,Trie树的内存消耗非常大
代码实现:
class TrieNode {
	private final int SIZE = 26;
	private final TrieNode[] children;
	private String item;
	
	public TrieNode() {
		children = new TrieNode[SIZE];
		item = "";
	}

	public String getItem() {
		return item;
	}

	public void setItem(String item) {
		this.item = item;
	}

	public TrieNode[] getChildren() {
		return children;
	}
	public TrieNode getChildren(int i) {
		if(i >= 26 || i < 0) throw new IllegalArgumentException();
		return children[i];
	}
	public void setChildren(int i, TrieNode node) {
		children[i] = node;
	}
}

public class Trie {
	/*根节点*/
	private TrieNode root;
	public Trie() {
		root = new TrieNode();
	}
	/*Insert a word into the trie
	 */
	public void insert(String word) {
		TrieNode cur = root;
		for(char ch : word.toCharArray()) {
			if(cur.getChildren(ch - 'a') == null)
				cur.setChildren(ch - 'a', new TrieNode());
			cur = cur.getChildren(ch - 'a');
		}
		cur.setItem(word);
	}
	/*return true if the word in the trie
	 */
	public boolean search(String word) {
		TrieNode cur = root;
		for(char ch : word.toCharArray()) {
			if(cur.getChildren(ch - 'a') == null)
				return false;
			cur = cur.getChildren(ch - 'a');
		}
		return cur.getItem().equals(word);
	}
	/*return true if there is any word i the trie
	 *  that starts with given prefix
	 */
	public boolean startWith(String prefix) {
		TrieNode cur = root;
		for(char ch : prefix.toCharArray()) {
			if(cur.getChildren(ch - 'a') == null)
				return false;
			cur = cur.getChildren(ch - 'a');
		}
		return true;
	}
	
	public static void main(String[] args) {
		 Trie trie = new Trie();
		 trie.insert("somestring");
		 System.out.println(trie.startWith("some"));
	}
}
改进或优化:
上述实现在当item是泛型类型并且其中含有一个key为String类型的成员变量是时,能够更好的扩展到符号表(symbols table)。
并不需要在每一个节点中存放该单词,而只需要使用一个布尔值表示该节点是不是单词的结尾即可。这样当许多单词有相同的前缀时能够节省许多空间。
class TrieNode {
	private final int SIZE = 26;
	TrieNode[] children;
	boolean isEndOfWord;
	
	public TrieNode() {
		children = new TrieNode[SIZE];
		isEndOfWord = false;
	}
}

public class Trie {
	/*根节点*/
	private TrieNode root;
	public Trie() {
		root = new TrieNode();
	}
	/*Insert a word into the trie
	 */
	public void insert(String word) {
		TrieNode cur = root;
		for(char ch : word.toCharArray()) {
			if(cur.children[ch - 'a'] == null)
				cur.children[ch - 'a'] =  new TrieNode();
			cur = cur.children[ch - 'a'];
		}
		cur.isEndOfWord = true;
	}
	/*return true if the word in the trie
	 */
	public boolean search(String word) {
		TrieNode cur = root;
		for(char ch : word.toCharArray()) {
			if(cur.children[ch - 'a'] == null)
				return false;
			cur = cur.children[ch - 'a'];
		}
		return cur.isEndOfWord;
	}
	/*return true if there is any word i the trie
	 *  that starts with given prefix
	 */
	public boolean startWith(String prefix) {
		TrieNode cur = root;
		for(char ch : prefix.toCharArray()) {
			if(cur.children[ch - 'a'] == null)
				return false;
			cur = cur.children[ch - 'a'];
		}
		return true;
	}
}
字典树的应用
1. 事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率
       1)有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
       2)给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
       3)给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。
       4)1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串
       5)寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
2. 字典树在串的快速检索中的应用。
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。
3. 字典树在“串”排序方面的应用
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出
用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可
4. 字典树在最长公共前缀问题的应用
1) 给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少?
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为最近公共祖先问题。
5. 排序

Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。
 举例: 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。


参考:
[1]. https://en.wikipedia.org/wiki/Trie
[2]. http://blog.csdn.net/hguisu/article/details/8131559
[3]. http://www.cnblogs.com/pony1993/archive/2012/07/18/2596730.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值