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

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

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

基本性质:

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

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

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


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

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

应为英文有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,很明显少了一个,所以,我们编码是需要注意一下。 其余看代码应该都懂的。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值