字典树(trie)

  Trie,又经常叫前缀树,字典树等等.实现思路如下

                                                          

       颜色为黑色的表示只是字符串中间的字符,为蓝色的表示是字符串末尾的那个字符,空白那个是树根节点.每个节点的意义实际上就是根,节点到这个结点所经过的每个字符组成的字符串.故而图中这个树实际上意味着已存在字符串"","to","tea","ted","ten","a","i","in","inn"


代码

public class Trie {
    private int SIZE = 26;
    private TrieNode root;// 字典树的根

    class TrieNode // 字典树节点
    {
        private int num;// 有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数
        private TrieNode[] son;// 所有的儿子节点
        private boolean isBlue;// 是不是最后字符
        private char val;// 节点的值

        TrieNode() {
            num = 1;
            son = new TrieNode[SIZE];
            isBlue = false;
        }
    }

    Trie() // 初始化字典树
    {
        root = new TrieNode();
    }

    // 建立字典树
    public void insert(String str) // 在字典树中插入一个单词
    {
        if (str == null || str.length() == 0) {
            return;
        }
        TrieNode node = root;
        char[] letters = str.toCharArray();// 将目标单词转换为字符数组
        for (int i = 0, len = str.length(); i < len; i++) {
            int pos = letters[i] - 'a';
            if (node.son[pos] == null) // 如果当前节点的儿子节点中没有该字符,则构建一个TrieNode并复值该字符
            {
                node.son[pos] = new TrieNode();
                node.son[pos].val = letters[i];
            } else // 如果已经存在,则将由根至该儿子节点组成的字符串模式出现的次数+1
            {
                node.son[pos].num++;
            }
            node = node.son[pos];
        }
        node.isBlue = true;
    }

    // 计算单词前缀的数量
    public int countPrefix(String prefix) {
        if (prefix == null || prefix.length() == 0) {
            return -1;
        }
        TrieNode node = root;
        char[] letters = prefix.toCharArray();
        for (int i = 0, len = prefix.length(); i < len; i++) {
            int pos = letters[i] - 'a';
            if (node.son[pos] == null) {
                return 0;
            } else {
                node = node.son[pos];
            }
        }
        return node.num;
    }

    // 在字典树中查找一个完全匹配的单词.
    public boolean has(String str) {
        if (str == null || str.length() == 0) {
            return false;
        }
        TrieNode node = root;
        char[] letters = str.toCharArray();
        for (int i = 0, len = str.length(); i < len; i++) {
            int pos = letters[i] - 'a';
            if (node.son[pos] != null) {
                node = node.son[pos];
            } else {
                return false;
            }
        }
        // 走到这一步,表明可能完全匹配,可能部分匹配,如果最后一个字符节点为末端节点,则是完全匹配,否则是部分匹配
        return node.isBlue;
    }

    public static void main(String[] args) {
        Trie tree = new Trie();
        String[] dictionaryData = { "hello", "student", "computer", "sorry", "acm", "people", "experienced", "who",
                "reminds", "everyday", "almost" };
        String[] txt = { "computer", "hello", "acm", "computer", "experienced", "computer", "hello", "eclipse",
                "student", "hello", "hello", "hello", "hello", "experienced" };
        // 构建字典
        for (String str : dictionaryData) {
            tree.insert(str);
        }
        Map<String, Integer> countMap = new HashMap<String, Integer>();
        for (int i = 0; i < txt.length; i++) {
            String lineTxt = txt[i];
            if (tree.has(lineTxt)) {
                if (countMap.containsKey(lineTxt)) {
                    countMap.put(lineTxt, countMap.get(lineTxt) + 1);
                } else {
                    countMap.put(lineTxt, 1);
                }
            } else {
                System.out.println(lineTxt + "不在字典中!");
            }
        }

        for (String s : countMap.keySet()) {
            System.out.println(s + "出现的次数" + countMap.get(s));
        }
    }
}

作用

1.字符串检索

      事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。

2.词频统计

    有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

3.排序

  Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。

4.字符串最长公共前缀

5.字符串搜索的前缀匹配

       trie树常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。

 

参考文章

Trie(前缀树/字典树)及其应用