【力扣上岸】一文搞定前缀树:从实现到应用

747 阅读3分钟

前言

力扣中有很多道题目可以用前缀树来解决。作为一种树的数据结构,前缀树被广泛应用在很多场景中,比如在搜索引擎中输入几个字,就会有自动补全的一系列选项出来。

本文将讲两道题,一道是前缀树的实现,另一道题是前缀树的应用。 题目链接: leetcode-cn.com/problems/im…

leetcode-cn.com/problems/de…

题目:实现前缀树

代码

class Node:
    def __init__(self):
        self.child = [None for _ in range(26)]
        self.isEnd = False
    
    def containKey(self, ch):
        return self.child[ord(ch) - ord('a')]
    
    def get(self, ch):
        return self.child[ord(ch) - ord('a')]
    
    def put(self, ch):
        self.child[ord(ch) - ord('a')] = Node()

class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = Node()
        
        
    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        p = self.root
        n = len(word)
        for i in range(n):
            if not p.containKey(word[i]):
                p.put(word[i])
            p = p.get(word[i])
        p.isEnd = True
        

    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        p = self.root
        for i in word:
            if not p.containKey(i):
                return False
            else:
                p = p.get(i)
        return p.isEnd

    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        p = self.root
        for i in prefix:
            if not p.containKey(i):
                return False
            else:
                p = p.get(i)
        return True

解读

第一步,就是实现一个新的类叫做Node,表示这棵树上的结点。有两个属性:child即是这个结点的子节点,用长度为26的列表来表示(有26个字母);isEnd表示是否为叶子结点。

插入一个词:遍历这个词。如果这个词有在树上,就不必添加结点;如果这个词不在树上,就添加结点。遍历结束后需要把最后一个结点的isEnd属性设置为True.

查找一个词:从树的根开始找,如果搜索不到结点,就返回False。遍历完这个词,就返回isEnd。如果isEnd是True,表明这个词存在;如果isEnd是False,表明这个词不在这棵树上。

判断是否有以“prefix”开头的词:和查找一个词的算法类似,但是遍历完这个词就直接返回True,不需要判断当前结点是否为叶子结点。

题目:前缀树的应用

解读

看到插入单词,查找单词就很容易联想到前缀树。然而这道题目的难点在于如何处理"."。出现了一个".",就意味着在这个位置,有多种选择,那么我们可以选择用深度优先搜索来判断这个单词是否存在。

我们可以用比较简单的方式来实现前缀树:用字典!每一个key都表示一个结点。

代码

class WordDictionary:
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.dic = {}

    def addWord(self, word: str) -> None:
        curr = self.dic
        for w in word:
            if w in curr:
                curr = curr[w]
            else:
                curr[w] = {}
                curr = curr[w]
        curr['end'] = True

    def search(self, word: str) -> bool:
        return self.dfs(word, self.dic, False, [])
    
    def dfs(self, word, node, cut, ls):
        if cut:
            return True
        curr = node
        for i, c in enumerate(word):
            if c == '.':
                for k, v in curr.items():
                    if k != 'end':
                        ls.append(self.dfs(word[i+1:], curr[k], cut, ls))
                return any(ls)
            if c not in curr:
                return False
            curr = curr[c]
        cut = 'end' in curr
        return cut

总结

弄明白字典树的实现方式可以让我们解决一系列问题。重点理解一下插入和查找过程,相信我们都可以做到举一反三!