博主今天新開一系列寫“結構”,簡單的單鏈表,普通隊列,普通棧,普通二叉樹就不寫了,今天從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
===================================================================================================