字典树及其实现

872 阅读2分钟

简介

字典数,又称为前缀树,是哈希树的一种,是一种有序树。其核心思想是空间换时间,利用字符串的公共前缀节省时间,减少字符串的比较。其典型应用包括排序、全文搜索、web引擎等。

结构

每个树节点维护两个元素,第一个是包含26个字典数节点的数组,第二个是每个节点是否是单词结尾的标识,我们以sea,sells,she三个单词为例构建字典树,其逻辑结构如下图左边所示,有部分公共字符是共用的,其物理结构如下图右边所示,维护一个26位的数组,若某个字符存在,则在数组的对应位置存放一个节点,这个节点又向下维护一个26位的数组,如此持续向下维护,直到字符串结束。

61657789501_.pic.jpg

代码实现

Java实现

class Trie {
    boolean isEnd;
    Trie[] next;
    public Trie() {
        isEnd = false;
        next = new Trie[26];
    }

    public void insert(String word) {
        Trie node = this;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (node.next[c - 'a'] == null) {
                node.next[c - 'a'] = new Trie();
            }
            node = node.next[c - 'a'];
        }
        node.isEnd = true;
    }

    public boolean search(String word) {
        Trie node = this;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            node = node.next[c - 'a'];
            if (node == null) {
                return false;
            }
        }
        return node.isEnd;
    }

    public boolean startsWith(String prefix) {
        Trie node = this;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            node = node.next[c - 'a'];
            if (node == null) {
                return false;
            }
        }
        return true;
    }
}

代码简要分析:

定义一个数组int[] trie和一个结束标识,并在构造方法中初始化,trie初始化为26位数组,结束标识默认为false。

insert()方法加入新字符串时,循环遍历每一个字符,如果该字符不存在,就在对应的位置新建一个Trie节点,如果存在,则当前节点直接向下移动,当字符串结束遍历时,将最后一个字符的结束标识设为true。

search()方法遍历字符串,若某个字符不存在,则这个字符串不存在,若最后一个字符的结束标识isEnd不是false,则表示这个字符串在字典树中找不到。

startsWith()方法和search()类似,只是不再以isEnd判读是否存在,在循环结束的时候,如果所有的字符都存在,则存在这个前缀。

go语言的实现和Java差不多。

Go实现

type Trie struct {
    child []*Trie
    isEnd bool
}


func Constructor() Trie {
    return Trie{make([]*Trie, 26), false}
}


func (this *Trie) Insert(word string)  {
    node := this
    for _, value := range word {
        if node.child[value - 'a'] == nil {
            node.child[value - 'a'] = &Trie{make([]*Trie, 26), false}
        }
        node = node.child[value - 'a']
    }
    node.isEnd = true
}


func (this *Trie) Search(word string) bool {
    node := this
    for _, value := range word {
        node = node.child[value - 'a']
        if node == nil {
            return false
        }
    }
    return node.isEnd
}


func (this *Trie) StartsWith(prefix string) bool {
    node := this
    for _, value := range prefix {
        node = node.child[value - 'a']
        if node == nil {
            return false
        }
    }
    return true
}