常用算法之Trie【字典树,前缀树】

Trie中文名又叫做字典树,前缀树等,因为其结构独有的特点,经常被用来统计,排序,和保存大量的字符串,经常见于搜索提示,输入法文字关联等,当输入一个值,可以自动搜索出可能的选择。当没有完全匹配的结果时,可以返回前缀最为相似的可能。

其实腾讯的面试题有一个:如何匹配出拼写单词的正确拼写。其实用匹配树非常合适。

基本性质:

1.根节点不含有字符,其余各节点有且只有一个字符。

2.根节点到某一节点中经过的节点存储的值连接起来就是对应的字符串。

3.每一个节点所有的子节点的值都不应该相同。

借用一下维基百科上的一张图片:

《常用算法之Trie【字典树,前缀树】》

看图还是蛮好理解的。但是我们应该怎么操作呢。

应为英文有26个字母,所以每个单词的每个位置都可能有26种取法,因此,我们可以为每个节点去26个子节点:

public class TrieNode {
    public static final int NUM_OF_LETTER = 26;

    private TrieNode[] childNodes = new TrieNode[NUM_OF_LETTER];
    private int freg;
    private boolean isEnd;
    private char varChar;

    /**
     * @param words
     * @return 不可用返回true  否则为可用
     */
    private static boolean checkWordsLenght(char[] words) {
        return (words.length == 0) || (words == null);
    }

    public static void addNode(TrieNode root, char[] words) {
        if (checkWordsLenght(words)) {
            return;
        }

        int index = words[0] - 'a';
        if (root.childNodes[index] == null) {
            root.childNodes[index] = new TrieNode();
            root.childNodes[index].varChar = words[0];
        }

        root.childNodes[index].freg++;
        words = Arrays.copyOfRange(words, 1, words.length);
        if (checkWordsLenght(words)) {
            root.isEnd = true;
            return;
        }
        addNode(root.childNodes[index], words);
    }

    public static void deleteNode(TrieNode root, char[] words) {
        if (checkWordsLenght(words) || (root == null)) {
            return;
        }

        int index = words[0] - 'a';
        if (root.childNodes[index] == null) {
            return;
        }

        root.childNodes[index].freg--;
        if (root.childNodes[index].freg == 0) {
            root.childNodes[index] = null;
        }

        words = Arrays.copyOfRange(words, 1, words.length);
        if (checkWordsLenght(words)) {
            root.childNodes[index] = null;
        }
        deleteNode(root.childNodes[index], words);
    }

    public static int getCountOfWords(TrieNode root, char[] words) {
        if (checkWordsLenght(words) || (root == null)) {
            return 0;
        }
        int index = words[0] - 'a';
        if (root.childNodes[index] == null) {
            return 0;
        }

        words = Arrays.copyOfRange(words, 1, words.length);
        if (checkWordsLenght(words)) {
            return root.freg;
        }

        return getCountOfWords(root.childNodes[index], words);
    }
}

里面有个很逗比的事情,我觉得可能是源代码编写人员犯错误。我们知道 Arrays.copyOfRange 有一个from和to的形参,意思是从索引为from开始复制,到索引to 结束,但是,看源码,我们可以看到它复制的内容长度是:to – from;当我们需要拷贝 0到 5的内容时,需要拷贝6个值,但计算确实5,很明显少了一个,所以,我们编码是需要注意一下。 其余看代码应该都懂的。


    原文作者:Trie树
    原文地址: https://blog.csdn.net/u010233260/article/details/45752777
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞