Trie 树 原理及实现

关于我的 Leetcode 题目解答,代码前往 Github:https://github.com/chenxiangcyr/leetcode-answers


Trie树原理

Trie树,即前缀树,字典树,单词查找树或键树。
典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

它的优点是:最大限度地减少无谓的字符串比较。

跟哈希表比较:

  • 最坏情况时间复杂度比 hash 表好
  • 没有冲突,除非一个 key 对应多个值(除key外的其他信息)
  • 自带排序功能(类似Radix Sort),中序遍历trie可以得到排序

Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

它有 3 个基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。

假设有 b,abc,abd,bcd,abcd,efg,hii 这6个单词,我们构建的树就是如下图这样的:


Trie 树 原理及实现_第1张图片
Trie 树

Trie树实现

Leetcode 208. Implement Trie (Prefix Tree)

Implement a trie with insert, search, and startsWith methods.

class Trie {
    class TrieNode {
        // R links to node children
        private TrieNode[] links;

        private boolean isEnd;

        public TrieNode() {
            links = new TrieNode[26];
        }

        public boolean containsKey(char ch) {
            return links[ch -'a'] != null;
        }
        
        public TrieNode get(char ch) {
            return links[ch -'a'];
        }
        
        public void put(char ch, TrieNode node) {
            links[ch -'a'] = node;
        }
        
        public void setEnd() {
            isEnd = true;
        }
        
        public boolean isEnd() {
            return isEnd;
        }
    }
    
    private TrieNode root;
    
    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    public void insert(String word) {
        char[] arr = word.toCharArray();
        
        TrieNode node = root;
        
        for (int i = 0; i < arr.length; i++) {
            
            if (!node.containsKey(arr[i])) {
                node.put(arr[i], new TrieNode());
            }
            
            node = node.get(arr[i]);
        }
        
        node.setEnd();
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode node = searchPrefix(word);
        return node != null && node.isEnd();
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode node = searchPrefix(prefix);
        return node != null;
    }
    
    // search a prefix or whole key in trie and
    // returns the node where search ends
    private TrieNode searchPrefix(String word) {
        char[] arr = word.toCharArray();
        
        TrieNode node = root;
        
        for (int i = 0; i < arr.length; i++) {
           if (node.containsKey(arr[i])) {
               node = node.get(arr[i]);
           } else {
               return null;
           }
        }
        
        return node;
    }
}

Trie树应用

  • 词频统计
  • 前缀匹配
  • 去重

适用范围:数据量大,重复多,但是数据种类小可以放入内存。

面试题有:

  • 有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序。

  • 1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。请问怎么设计和实现?

  • 寻找热门查询:查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个,每个不超过255字节。

  • 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。

  • 用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度),然后是找出出现最频繁的前10个词,可以用堆来实现。

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

  • 每个结点增加个count变量,查询、插入时维护count变量进行词频统计,用一个最小堆进行维护最高频的100个词的频率。


引用:
Trie(前缀树/字典树)及其应用
大数据处理——Trie树

你可能感兴趣的:(Trie 树 原理及实现)