trie樹-前綴樹


http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html

trie,又稱前綴樹字典樹,是一種有序

一:概念

     下面我們有and,as,at,cn,com這些關鍵詞,那么如何構建trie樹呢?

從上面的圖中,我們或多或少的可以發現一些好玩的特性。

      第一:根節點不包含字符,除根節點外的每一個子節點都包含一個字符。

      第二:從根節點到某一節點,路徑上經過的字符連接起來,就是該節點對應的字符串。

      第三:每個單詞的公共前綴作為一個字符節點保存。

 

二:使用范圍

     既然學Trie樹,我們肯定要知道這玩意是用來干嘛的。

     第一:詞頻統計。

            可能有人要說了,詞頻統計簡單啊,一個hash或者一個堆就可以打完收工,但問題來了,如果內存有限呢?還能這么

             玩嗎?所以這里我們就可以用trie樹來壓縮下空間,因為公共前綴都是用一個節點保存的

     第二: 前綴匹配

            就拿上面的圖來說吧,如果我想獲取所有以"a"開頭的字符串,從圖中可以很明顯的看到是:and,as,at,如果不用trie樹,

            你該怎么做呢?很顯然朴素的做法時間復雜度為O(N2) ,那么用Trie樹就不一樣了,它可以做到h,h為你檢索單詞的長度,

            可以說這是秒殺的效果。

舉個例子:現有一個編號為1的字符串”and“,我們要插入到trie樹中,采用動態規划的思想,將編號”1“計入到每個途徑的節點中,

              那么以后我們要找”a“,”an“,”and"為前綴的字符串的編號將會輕而易舉。

  1 package jd;
  2 
  3 import java.util.HashMap;
  4 import java.util.Map.Entry;
  5 
  6 /**
  7  * 前綴樹的實現
  8  * 
  9  * @author li
 10  * 
 11  */
 12 public class TrieTree {
 13     /**
 14      * 前綴樹節點
 15      * 
 16      * @author li
 17      * 
 18      */
 19     private class Node {
 20         private int prefixNum;// 以該字符串為前綴的字符數
 21         private Node[] childNodes;
 22         private boolean isLeaf;// 是否為單詞
 23         private int dumplicateNum;// 該字符串重復出現的次數(單詞重復出現次數)
 24 
 25         public Node() {
 26             prefixNum = 0;
 27             isLeaf = false;
 28             dumplicateNum = 0;
 29             childNodes = new Node[26];
 30         }
 31     }
 32 
 33     private Node root;// 根節點
 34 
 35     public TrieTree() {
 36         root = new Node();
 37 
 38     }
 39 
 40     public void insert(String word) {
 41         insert(this.root, word);
 42     }
 43 
 44     /**
 45      * 插入單詞
 46      * 
 47      * @param root
 48      * @param word
 49      */
 50     public void insert(Node root, String word) {
 51         word = word.toLowerCase();
 52         char[] chars = word.toCharArray();
 53         for (int i = 0; i < chars.length; i++) {
 54             int index = chars[i] - 'a';// 對應的下標
 55             if (root.childNodes[index] == null) {
 56                 root.childNodes[index] = new Node();
 57             }
 58             root.childNodes[index].prefixNum++;
 59             if (i == chars.length - 1) {
 60                 root.childNodes[index].isLeaf = true;// 單詞
 61                 root.childNodes[index].dumplicateNum++;// 該單詞出現的個數
 62             }
 63             root = root.childNodes[index];
 64         }
 65 
 66     }
 67 
 68     /**
 69      * 獲取以prefixStr為前綴的字符串
 70      * 
 71      * @param prefixStr
 72      * @return
 73      */
 74     private HashMap<String, Integer> getPrefixString(Node root, String prefixStr) {
 75         prefixStr = prefixStr.toLowerCase();
 76         HashMap<String, Integer> map = new HashMap<String, Integer>();
 77         char[] prefixs = prefixStr.toCharArray();
 78         for (int i = 0; i < prefixs.length; i++) {
 79             int index = prefixs[i] - 'a';
 80             if (root.childNodes[index] != null) {
 81                 root = root.childNodes[index];
 82             } else {
 83                 return null;
 84             }
 85         }
 86         dfs(root, prefixStr, map);
 87         return map;
 88 
 89     }
 90 
 91     public HashMap<String, Integer> getPrefixString(String prefixStr) {
 92 
 93         return getPrefixString(root, prefixStr);
 94 
 95     }
 96 
 97     /**
 98      * 深度優先遍歷
 99      * 
100      * @param root
101      *            :根節點
102      * @param map
103      */
104     private HashMap<String, Integer> dfs(Node root, String prefixs,
105             HashMap<String, Integer> map) {
106         if (root == null) {
107             return null;
108         }
109         if (root.isLeaf) {
110             map.put(prefixs, root.dumplicateNum);
111 
112         }
113         Node[] nodes = root.childNodes;
114         for (int i = 0; i < nodes.length; i++) {
115             if (nodes[i] != null) {
116                 char c = (char) (i + 'a');
117                 String tempStr = prefixs + c;
118                 dfs(root.childNodes[i], tempStr, map);
119             }
120 
121         }
122         return map;
123 
124     }
125 
126     /**
127      * 遍歷所有的字符串
128      * 
129      * @return
130      */
131     public HashMap<String, Integer> dfs() {
132         HashMap<String, Integer> map = new HashMap<String, Integer>();
133         return dfs(this.root, "", map);
134     }
135 
136     /**
137      * 刪除節點
138      * 
139      * @param word
140      */
141     public boolean delete(String word) {
142         // 不存在該單詞則刪除
143         Node p = root;
144         if (!searchWord(word)) {
145             return false;
146         }
147         word = word.toLowerCase();
148         char[] chars = word.toCharArray();
149         for (int i = 0; i < chars.length; i++) {
150             int index = chars[i] - 'a';
151             Node[] nodes = p.childNodes;
152             if (nodes[index].prefixNum == 1) {
153                 nodes[index] = null;
154                 return true;
155             } else {
156                 nodes[index].prefixNum--;
157                 p = nodes[index];
158             }
159         }
160         if (p.dumplicateNum == 1) {
161             p.isLeaf = false;
162         }
163         p.dumplicateNum--;
164         return true;
165 
166     }
167 
168     /**
169      * 
170      * @param root
171      * @param word
172      * @return
173      */
174     private boolean searchWord(Node root, String word) {
175         if (root == null) {
176             return false;
177         }
178         word = word.toLowerCase();
179         char[] chars = word.toCharArray();
180 
181         for (int i = 0; i < chars.length; i++) {
182             int index = chars[i] - 'a';
183             Node[] nodes = root.childNodes;
184             if (nodes[index] != null) {
185                 root = nodes[index];
186             } else {
187                 return false;
188             }
189 
190         }
191         if (!root.isLeaf) {
192             return false;
193         }
194         return true;
195 
196     }
197 
198     public boolean searchWord(String word) {
199         return searchWord(root, word);
200 
201     }
202 
203     public static void main(String[] args) {
204         TrieTree trieTree = new TrieTree();
205         trieTree.insert("gab");
206         trieTree.insert("gb");
207         trieTree.insert("gb");
208         trieTree.insert("gbc");
209         trieTree.insert("gbc");
210         trieTree.insert("go");
211         trieTree.insert("go");
212         trieTree.insert("goa");
213         for (Entry<String, Integer> entry : trieTree.getPrefixString("g")
214                 .entrySet()) {
215             System.out.println(entry.getKey() + " : " + entry.getValue());
216 
217         }
218         trieTree.delete("gb");
219         System.out.println("***************");
220         for (Entry<String, Integer> entry : trieTree.getPrefixString("g")
221                 .entrySet()) {
222             System.out.println(entry.getKey() + " : " + entry.getValue());
223 
224         }
225 
226     }
227 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM