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