我们有如下需求,给出4*4
的字母表,我们可以通过把相邻(横向、竖向、斜向都算相邻)的字母连接起来组成一个单词,要找到所有单词。像下图画的WAIT
就算是一个单词。假设我们已经有所有单词的词库。
容易想到的方法是我们可以从左上角[0,0]
开始,用DFS
的方法进行搜索;之后再从[0,1]
开始,以此类推。因为DFS
过程中,每新增一个字母,我们都要去跟词库做一次比较,所以词库如何实现对程序的运行效率有很大的影响。
需要一个快速查找有没有某元素的数据结构,我们容易想到哈希表,但是在这个需求里,哈希表不能满足需求。就像上面WAIT
的例子,搜索的时候应该是W -> WA -> WAI -> WAIT
这样,如果词库使用哈希表实现,只能告诉我们WAIT
存在,我们的程序依然要处理所有的序列,因为词库不能告诉我们W
开头有没有单词,甚至WAIT
之后还有没有单词。所以在找到WAIT
之后,搜索也不能停下来,否则就可能漏掉正确答案。
如果词库在搜W的时候告诉我们没有搜下去的必要了,那么我们就可以放弃继续搜索,转而搜索其他序列,大大提高效率。
所以我们需要的词库应该满足下面的需求
写成程序,我们的词库接口应该是这个样子
public interface Vocabulary {
boolean add(String word);
boolean isPrefix(String prefix);
boolean contains(String word);
}
字典树就是一种可以满足以上需求的数据结构。顾名思义,字典树是多叉树,每个节点存储了下一个字符的集合,以及此结点是不是一个单词的结尾
其中上图中,蓝色的结点就是单词的结尾。
如果我们查到这个节点,发现它是单词的结尾,就说明目前的字符序列对应了一个单词;如果此结点还有子结点,说明存在以此字符序列开头的更长的单词,可以继续往下搜索。
添加单词时也一样,查询时碰到不存在的结点就添加,在达到末尾时标记结点为单词的结尾。
比较完整的实现可以参考这里