当前位置:   article > 正文

Trie 字典树的两种实现方式_trie 实现

trie 实现

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

上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。从上图可以归纳出Trie树的基本性质:

  • 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  • 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符互不相同。
  • 从第一字符开始有连续重复的字符只占用一个节点,比如上面的to,和ten,中重复的单词t只占用了一个节点。

字典树的实现有递归与非递归的两种方式:

非递归:

非递归的实现也有两种方式,一种是利用自己创建的节点来实现,具体实现如下:

  1. public class Trie {
  2. public static void main(String[] args) {
  3. Trie trie = new Trie();
  4. trie.add("apple");
  5. System.out.println(trie.contains("apple"));
  6. System.out.println(trie.isPrefix("app"));
  7. }
  8. private class TreeNode{
  9. public boolean isWord;
  10. public TreeMap<Character,TreeNode> next;
  11. public TreeNode(){
  12. this(false);
  13. next = new TreeMap<>();
  14. }
  15. public TreeNode(boolean isWord){
  16. this.isWord = isWord;
  17. }
  18. }
  19. private TreeNode root;
  20. private int size;
  21. public Trie(){
  22. this.root = new TreeNode();
  23. this.size = 0;
  24. }
  25. public int getSize(){
  26. return size;
  27. }
  28. public void add(String str){
  29. TreeNode cur = root;
  30. for(int i=0;i<str.length();i++){
  31. char c = str.charAt(i);
  32. if(cur.next.get(c) == null){
  33. //新建节点
  34. cur.next.put(c, new TreeNode());
  35. }
  36. //否则,就直接走到该节点位置即可
  37. cur = cur.next.get(c);
  38. }
  39. if(!cur.isWord){
  40. //确定cur是新的单词
  41. cur.isWord = true;
  42. size++;
  43. }
  44. }
  45. public boolean contains(String str){
  46. TreeNode cur = root;
  47. for(int i=0;i<str.length();i++){
  48. char c = str.charAt(i);
  49. if(cur.next.get(c) == null) return false;
  50. cur = cur.next.get(c);
  51. }
  52. return cur.isWord;
  53. }
  54. public boolean isPrefix(String prefix){
  55. TreeNode cur = root;
  56. for(int i=0;i<prefix.length();i++){
  57. char c = prefix.charAt(i);
  58. if(cur.next.get(c) == null) return false;
  59. cur = cur.next.get(c);
  60. }
  61. return true;
  62. }
  63. }

这种实现方式是利用自己创建的节点实现的。每一个Trie对象内部都存在一个TreeNode对象与size对象。其中TreeNode对象内部存在着一个TreeMap,TreeMap内存储着每一个字符与下一个节点的对应关系。

当然,也有另一种实现方式:

  1. class Trie {
  2. private Trie[] children;
  3. private boolean isWord;
  4. public Trie() {
  5. children = new Trie[26];
  6. isWord = false;
  7. }
  8. public void insert(String word) {
  9. Trie node = this;
  10. for(char c : word.toCharArray()){
  11. int idx = c - 'a';
  12. if(node.children[idx] == null){
  13. node.children[idx] = new Trie();
  14. }
  15. node = node.children[idx];
  16. }
  17. node.isWord = true;
  18. }
  19. public boolean search(String word) {
  20. Trie node = searchPrefix(word);
  21. return node != null && node.isWord;
  22. }
  23. public boolean startsWith(String prefix) {
  24. return searchPrefix(prefix) != null;
  25. }
  26. public Trie searchPrefix(String word){
  27. Trie node = this;
  28. for(char c : word.toCharArray()){
  29. int idx = c - 'a';
  30. if(node.children[idx] != null){
  31. node = node.children[idx];
  32. }else{
  33. return null;
  34. }
  35. }
  36. return node;
  37. }
  38. }
  39. /**
  40. * Your Trie object will be instantiated and called as such:
  41. * Trie obj = new Trie();
  42. * obj.insert(word);
  43. * boolean param_2 = obj.search(word);
  44. * boolean param_3 = obj.startsWith(prefix);
  45. */

这个实现方式是每一个Trie对象内部存储自己的Trie[]数组,由此实现节点的对应关系。

递归
  1. public void recursionAdd(String word) {
  2. Node cur = root;
  3. add(root, word, 0);
  4. }
  5. /**
  6. * 递归写法调用方法实现递归添加
  7. *
  8. * @param node 传入要进行添加的节点
  9. * @param word 传入要进行添加的单词
  10. */
  11. public void add(Node node, String word, int index) {
  12. // 确定终止条件,这个终止条件在没加index这个参数时,很难确定
  13. // 此时一个单词已经遍历完成了,如果这个结束节点没有标记为单词,就标记为单词
  14. if (!node.isWord && index == word.length()) {
  15. node.isWord = true;
  16. size++;
  17. }
  18. if (word.length() > index) {
  19. char addLetter = word.charAt(index);
  20. // 判断trie的下个节点组中是否有查询的字符,如果没有,就添加
  21. if (node.next.get(addLetter) == null) {
  22. node.next.put(addLetter, new Node());
  23. }
  24. // 基于已经存在的字符进行下个字符的递归查询
  25. add(node.next.get(addLetter), word, index + 1);
  26. }
  27. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/994730
推荐阅读
  

闽ICP备14008679号