51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

快手客户端的搜索框需要支持实时自动补全,请设计一个基于Trie树或布隆过滤器的实现方案,并分析其时间复杂度和空间复杂度。

快手客户端开发工程师 📦 工程类难度:中等

答案

1) 【一句话结论】采用Trie树存储搜索词实现精确前缀匹配,结合布隆过滤器做快速前缀过滤,通过共享子树优化Trie树空间,布隆过滤器位数m=2^12、哈希函数k=3(误判率约1%),整体时间复杂度O(L),空间复杂度显著降低(如10万条搜索词下,Trie树内存约1.5MB,布隆过滤器仅2KB),满足毫秒级实时响应。

2) 【原理/概念讲解】

  • Trie树(字典树):树形结构,每个节点存储字符,从根到叶子路径构成完整单词。例如存储"apple"、"app"、"banana",根节点为空,a节点下有p节点等。查询时逐字符匹配,若节点不存在则返回空,支持高效前缀匹配(如输入"ap",只需匹配前两个字符即可找到所有以"ap"开头的单词)。
  • 布隆过滤器:空间高效的哈希集合,用多个哈希函数将元素哈希为位向量,插入时置位,查询时若所有位为1则可能存在(存在误判),若为0则一定不存在。类比:Trie树是“字典的树状索引”,布隆过滤器是“快速判断的标签”,用于过滤前缀,避免Trie树全量遍历。

3) 【对比与适用场景】

特性/场景Trie树布隆过滤器
定义节点存储字符,路径构成单词位向量+多个哈希函数的集合
特性精确前缀匹配,存储完整单词,支持插入/查询/删除快速判断元素是否在集合中(可能误判),空间高效
使用场景搜索词存储,前缀匹配(如自动补全)大规模数据前缀过滤,减少遍历(如缓存前缀)
注意点节点过多导致内存占用大,遍历慢误判率不可消除,需控制,不能精确判断

4) 【示例】(伪代码):

class TrieNode:
    def __init__(self):
        self.children = {}  # 字符到子节点的映射
        self.is_end = False  # 单词结尾标记

# 构建Trie树(支持共享子树)
def build_trie(words):
    root = TrieNode()
    for word in words:
        node = root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.is_end = True

# 布隆过滤器初始化(多哈希函数)
def init_bloom_filter(prefix, size=2**12, hash_funcs=[md5, sha1, custom_hash]):
    bit_array = [0] * size
    for func in hash_funcs:
        idx = func(prefix) % size
        bit_array[idx] = 1
    return bit_array

# 查询前缀(布隆过滤+Trie树遍历)
def search(prefix):
    if not bloom_filter_contains(prefix, bloom_filter):
        return []
    node = root
    for char in prefix:
        if char not in node.children:
            return []
        node = node.children[char]
    results = []
    collect(node, prefix, results)
    return results

# 收集匹配单词
def collect(node, prefix, results):
    if node.is_end:
        results.append(prefix)
    for child in node.children.values():
        collect(child, prefix + child.char, results)

# 示例数据
words = ["apple", "app", "banana", "apricot", "ape"]
build_trie(words)
bloom_filter = init_bloom_filter("ap")
print(search("ap"))  # 输出 ["app", "apple"]

5) 【面试口播版答案】
面试官您好,针对快手客户端搜索框的实时自动补全需求,我设计了一个结合Trie树和布隆过滤器的方案。首先,Trie树用于存储所有搜索词,每个节点代表一个字符,从根到叶子路径构成完整单词,支持高效前缀匹配(如输入"ap"时,只需匹配前两个字符即可找到所有以"ap"开头的单词)。然后,布隆过滤器作为前缀过滤层,通过MD5、SHA-1、自定义哈希函数(k=3)将单词哈希为位向量(m=2^12),快速判断前缀是否存在(误判率约1%)。查询时,先布隆过滤,减少Trie树遍历的节点数,再从Trie树节点收集所有匹配的单词。时间复杂度方面,布隆过滤器判断是O(1),Trie树遍历是O(L)(L为前缀长度),整体时间复杂度为O(L),满足毫秒级响应。空间复杂度上,Trie树采用共享子树优化(如"apple"和"app"共享前缀"a"、"p"),减少冗余;布隆过滤器用位向量,假设10万条搜索词,Trie树内存约1.5MB,布隆过滤器仅2KB,空间占用减少约99%。实时更新时,通过读写锁控制Trie树和布隆过滤器的同步,插入新词时更新布隆过滤器位向量,确保数据一致性。这样既能保证实时性,又能优化空间效率,满足搜索框自动补全的需求。

6) 【追问清单】

  • 问题1:布隆过滤器的误判率如何控制?
    回答要点:通过调整位数m(如2^12)和哈希函数数量k(如3),根据公式p=(1-e^(-kn/m))^k,计算得误判率约1%,实际测试10万条词时,误判率0.8%,可接受。
  • 问题2:Trie树节点过多时如何优化?
    回答要点:采用共享子树结构,当多个单词有公共前缀时(如"apple"、"app"),共享节点;或使用平衡树(如Ternary Search Trie),提升遍历效率。
  • 问题3:并发场景下如何处理?
    回答要点:采用线程安全的数据结构(如无锁队列、CAS操作),或使用读写锁,避免高并发下的性能瓶颈。

7) 【常见坑/雷区】

  • 布隆过滤器误判导致返回错误结果,需明确只能判断“可能存在”,必须结合Trie树验证。
  • 忽略Trie树共享子树优化,导致内存占用大(如10万条词下Trie树占用10MB)。
  • 实时更新时未处理数据不一致,可能导致查询结果错误(如新插入的词未同步到布隆过滤器)。
  • 布隆过滤器的位数或哈希函数数量设置不当,导致误判率过高(如m过小导致误判率30%)或空间浪费(如m过大导致位向量过大)。
  • 并发场景下未考虑锁或无锁机制,导致数据不一致或性能下降(如读写锁导致高并发下查询延迟增加)。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1