元婴期

15. 堆是什么?最大堆和最小堆的应用场景是什么?

是一种特殊的二叉树,满足以下性质:

  • 最大堆:每个节点的值都大于或等于其子节点的值,根节点是整个堆的最大值。

    如下结构所示:

               9
             /  \
            5    6
           / \   /
          3  4  2
    
  • 最小堆:每个节点的值都小于或等于其子节点的值,根节点是整个堆的最小值。

    如下结构所示:

              1
             / \
            3   2
           / \  /
          6  5 9
    

应用场景

  • 最大堆:用于实现优先级队列(如任务调度器),可以快速获取优先级最高的元素。

  • 最小堆:用于求解最小值问题,或者在需要按顺序输出最小元素时(如最短路径算法的实现)。


16. 如何实现堆排序?解释其时间复杂度。

堆排序 是一种基于堆的数据结构进行排序的算法,利用最大堆或最小堆的性质实现升序或降序排序。

实现步骤

  • 将数组构建为最大堆(升序)或最小堆(降序)。

  • 取出堆顶元素(最大或最小),将其放到已排序区域,并将剩余的元素重新调整为堆。

  • 重复上述操作,直到所有元素排序完成。

时间复杂度

  • 构建堆 的时间复杂度为 O(n)

  • 每次调整堆的时间复杂度为 O(log n),重复 n 次,整体时间复杂度为 O(n log n)


17. 什么是跳表(Skip List)?跳表的时间复杂度是多少?

跳表 是一种支持快速查找、插入和删除的链表结构,它在有序链表的基础上增加多层索引,通过跳跃式的搜索提高效率。

假设我们有一个有序的链表存储以下数据:1, 3, 5, 7, 9, 13, 17, 21, 25。我们可以构建如下的跳表:

Level 2:      1 -------- 9 -------- 17 -------- 25
Level 1:      1 ---- 5 ---- 9 ---- 13 ---- 17 ---- 21 ---- 25
Level 0:      1 -- 3 -- 5 -- 7 -- 9 -- 13 -- 17 -- 21 -- 25
  • Level 0 是完整的链表,包含所有元素。

  • Level 1Level 0 的缩减版本,跳跃性连接部分节点,减少查找步骤。

  • Level 2Level 1 的缩减版本,跳跃性连接更少的节点。

假设我们要在这个跳表中查找数字 13,过程如下:

  • 从最高层 Level 2 开始,比较 13 和当前节点的值:

    • 当前节点是 1,向右跳到 9

    • 13 > 9,继续跳到 17。发现 13 < 17,回到上一层。

  • 进入 Level 1,继续比较:

    • 当前节点是 9,向右跳到 13

    • 找到了 13

通过跳表结构,我们跳过了部分节点,减少了比较的次数,从而加速查找。

时间复杂度

  • 查找、插入、删除 的平均时间复杂度是 O(log n),因为每一层跳表大约减少一半的元素,类似于二分查找。

  • 最坏情况下,时间复杂度是 O(n)

跳表常用于实现有序数据结构,如 Redis 中的有序集合。


18. Trie 树是什么?它的应用场景有哪些?

Trie 树 是一种用于高效存储和查找字符串的数据结构,每个节点表示一个字符,路径表示一个字符串。

假设我们要存储以下单词集合:

  • cat

  • cap

  • bat

  • bake

Trie 树的结构如下:

          (root)
          /    \
         c      b
        / \     / \
       a   a   a   a
      /     \     \
     t*      p*    t*
                 / 
                k  
               /  
              e*
  • 根节点(root)Trie 树从一个空的根节点开始。

  • 每个单词的字符路径

    • cat:路径是 c -> a -> t

    • cap:路径是 c -> a -> p。注意,这与 cat 共享前缀 c -> a

    • bat:路径是 b -> a -> t

    • bake:路径是 b -> a -> k -> e。注意,b -> a 共享在 bat 中。

  • 终止节点:每个单词的最后一个字符节点代表一个完整单词。例如,tcat 中,pcap 中,ebake 中。

应用场景

  • 前缀匹配:快速查找具有共同前缀的字符串集。

  • 自动补全:输入法、搜索引擎中的自动补全功能。

  • 字典树:快速检索单词,如拼写检查器。

Trie 树中存储单词 appleapp 时,appapple 的前缀,两个单词共享相同的前缀节点。


19. 什么是并查集?如何用并查集实现集合的合并与查找?

并查集(Union-Find) 是一种用于处理动态连通性问题的数据结构,适用于快速合并集合和查询元素是否属于同一集合。

基本操作

  • 查找(Find):找到某个元素所在集合的代表元素(根节点)。

  • 合并(Union):将两个集合合并为一个集合。

实现

  • 路径压缩:在查找过程中,将沿途节点直接指向根节点,优化后续查询。

  • 按秩合并:每次合并时,将节点数较少的树合并到节点数较多的树中,减少树的高度。

并查集用于解决图中的连通性问题,如判断图中是否存在环,或解决网络中节点是否属于同一连通区域。


20. 什么是哈夫曼编码?它的原理和应用是什么?

哈夫曼编码(Huffman Coding) 是一种贪心算法,用于构建最优前缀编码,常用于数据压缩。

原理

  • 根据字符出现的频率构建哈夫曼树,频率越高的字符对应的编码越短。

  • 使用二进制编码表示字符,保证没有一个编码是另一个编码的前缀。

哈夫曼编码示例

假设我们要对字符串 BCAADDDCCACACAC 进行哈夫曼编码。

第一步:统计字符频率

字符:  A   B   C   D
频率:  5   1   6   3

第二步:构建哈夫曼树

  • 将每个字符看作一个叶子节点,频率作为权重。

  • 合并频率最小的两个节点。

初始节点:

A: 5  B: 1  C: 6  D: 3
  • 合并 BD,新节点频率为 1 + 3 = 4,结果如下:
       *
      / \
     B   D

新节点权重 4:B 和 D
  • 再合并 A 和新节点 (B+D),结果如下:
        *
       / \
      A   *
         / \
        B   D
  • 最后合并上面的结果和 C
         *
        / \
       *   C
      / \
     A   *
        / \
       B   D

第三步:生成编码

根据哈夫曼树,从根节点出发生成二进制编码。每往左走一步记作 0,每往右走一步记作 1

生成的编码如下:

  • A 的编码:00

  • B 的编码:110

  • C 的编码:1

  • D 的编码:111

第四步:编码文本

将字符串 BCAADDDCCACACAC 转换成哈夫曼编码:

B -> 110
C -> 1
A -> 00
A -> 00
D -> 111
D -> 111
D -> 111
C -> 1
C -> 1
A -> 00
C -> 1
A -> 00
C -> 1
A -> 00
C -> 1

压缩后的哈夫曼编码是:

110 1 00 00 111 111 111 1 1 00 1 00 1 00 1

应用场景

  • 数据压缩:如 ZIP 文件压缩、JPEG 图像压缩。

  • 通信:用于减少数据传输时的带宽占用。


21. 布隆过滤器(Bloom Filter)是什么?它有哪些应用场景?

布隆过滤器(Bloom Filter) 是一种概率型数据结构,用于高效判断某个元素是否存在于集合中,但允许一定的误判率(即存在误判为真,但不存在误判为假)。

原理

  • 布隆过滤器使用多个哈希函数将元素映射到一个位数组上。当查询一个元素时,只需检查所有哈希函数映射的位置是否都为 1

  • 如果有任何位置为 0,则该元素肯定不在集合中;如果所有位置都为 1,元素可能存在(存在误判)。

假设我们有一个位数组初始为全 0,并使用 3 个哈希函数。

插入数据 apple
  • apple3 个哈希函数分别映射到位数组的第 258 位。

  • 我们将这 3 个位置的值设置为 1

    此时位数组状态为:

      [0, 0, 1, 0, 0, 1, 0, 0, 1, 0]
    
查询数据 apple
  • 再次使用 3 个哈希函数对 apple 进行哈希映射,依然会映射到位数组的第 258 位。

  • 检查这 3 个位置的值是否为 1。由于它们都是 1,我们判断 apple 可能存在。

查询数据 banana
  • 使用 3 个哈希函数对 banana 进行哈希映射,假设映射到位数组的第 137 位。

  • 检查位数组中这 3 个位置。由于第 1 位为 0,我们可以确定 "banana" 一定不存在。

应用场景

  • 网页黑名单检测:判断一个 URL 是否在黑名单中。

  • 缓存穿透:在分布式缓存系统中,布隆过滤器可以避免查询数据库的无效请求。

  • 垃圾邮件过滤:判断某个邮件是否为垃圾邮件。

results matching ""

    No results matching ""