博主今天新开一系列写“结构”,简单的单链表,普通队列,普通栈,普通二叉树就不写了,今天从Trie树写起。
Trie树(又叫字典树,前缀树,单词查找树,键树)是一种树形数据结构,直接来看图:
我们来看看Trie树的特点:根节点为空值,剩下每一个节点保存一个字母。知道这些就够了!
我们再来看看这棵树能干什么?如果从根节点遍历到某一个节点把路径节点的值连在一起就构成了一个字符串,利用这个特点很容易想到这棵树的第一个功能能帮我们查找某一个单词是否在树中(需要在每一个节点设置一个标志,表示从根节点到此节点是否构成一个单词);如果该单词存在,我们可以利用它实现第二个功能:去除重复单词;同样如果该词存,在我们还可以看出它的第三个功能:统计单词频率;因为这是一个树形结构我们利用这个特点很容易看出它的第四个功能能帮我们查找N个单词的最长公共前缀;如果我们按顺序遍历输出整棵树,发现它的第五个功能:对字符串排序。
这棵树创建看起来比较容易,就有一个问题需要我们考虑:父节点如何保存孩子节点? 主要有两种方式供大家参考:
1.因为是英文字符,我们可以用Node[26]来保存孩子节点(如果是数字我们可以用Node[10]),这种方式最快,但是并不是所有节点都会有很多孩子,所以这种方式浪费的空间太多
2.用一个链表根据需要动态添加节点。这样我们就可以省下不小的空间,但是缺点是搜索的时候需要遍历这个链表,增加了时间复杂度。
下面我用数组保存孩子节点的方式实现的trie树:
class TrieNode{//结点类 private static final int NUMBER = 26; private char _value; private boolean _isWord;//从根节点到这个节点存不存在一个单词 TrieNode[] _children = new TrieNode[NUMBER];//子结点集合 public TrieNode(char c) { this.setValue(c); } public char getValue() { return _value; } public void setValue(char _value) { this._value = _value; } public boolean isWord() { return _isWord; } public void setIsWord(boolean _isWord) { this._isWord = _isWord; } } public class TrieTree { static String[] _words = {"add","am","good","the","think"};//待插入单词 private boolean searchWord(TrieNode _root, String _word) { if(null == _root || null == _word || "".equals(_word)) return false; char[] cs = _word.toCharArray();//将字符串转化为字符数组 for(int i = 0; i < cs.length; i++){ int index; if(cs[i] >= 'A' && cs[i] <= 'Z'){ index = cs[i]-'A'; } else if(cs[i] >= 'a' && cs[i] <= 'z') index = cs[i] - 'a'; else return false; TrieNode child_node = _root._children[index]; if(null != child_node){//找到相同字符 if(child_node.isWord())//如果找到该单词 return true; } if(null == child_node)//如果在i层没找到相同字符 return false; _root = child_node;//重设根节点 } return false; } private void insertIntoTree(TrieNode _root, String _word) {//插入一个单词 if(null == _root || null == _word || "".equals(_word)) return; char[] cs = _word.toCharArray();//将字符串转化为字符数组 for(int i = 0; i < cs.length; i++){ int index;//对应的索引值 if(cs[i] >= 'A' && cs[i] <= 'Z'){ index = cs[i]-'A'; } else if(cs[i] >= 'a' && cs[i] <= 'z') index = cs[i] - 'a'; else return; TrieNode child_node = _root._children[index]; if(null == child_node){//如果没找到 TrieNode new_node = new TrieNode(cs[i]);//创建新节点 if(i == cs.length-1)//如果遍历到该单词最后一个字符 new_node.setIsWord(true);//把该单词存在树中 _root._children[index] = new_node;//连接该节点 _root = new_node; }else _root = child_node;//更新树根 } } private void printTree(TrieNode _root,char[] _word,int index) { if(_root == null) return; if(_root.isWord()){//如果根节点到此节点构成一个单词则输出 for(char c : _word){ if(c != ' ') System.out.print(c); } System.out.println(); } for(TrieNode node : _root._children){//遍历树根孩子节点 if(node != null){//回溯法遍历该树 _word[index++] = node.getValue(); printTree(node,_word,index); _word[index] = ' '; index--; } } } public static void main(String[] args){ TrieTree _tree = new TrieTree();//创建一棵树 TrieNode _root = new TrieNode(' ');//创建根节点 for(String word : _words)//插入单词 _tree.insertIntoTree(_root,word); char[] _word = new char[20]; _tree.printTree(_root,_word,0);//打印树中单词 boolean status = _tree.searchWord(_root,"think");//查询树中是否存在某单词 System.out.println(status); } }
==================================================================================================
作者:nash_ 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/nash_/article/details/8227610
===================================================================================================