算法改进流程——以“小S的倒排索引”为例 | 豆包MarsCode AI刷题

211 阅读4分钟

小S的倒排索引

问题描述

小S正在帮助她的朋友们建立一个搜索引擎。为了让用户能够更快地找到他们感兴趣的帖子,小S决定使用倒排索引。倒排索引的工作原理是:每个单词都会关联一个帖子ID的列表,这些帖子包含该单词,且ID按从小到大的顺序排列。
例如,单词“夏天”可能出现在帖子1、帖子3和帖子7中,那么这个单词的倒排链就是 [1, 3, 7]。如果用户想同时找到包含“夏天”和“海滩”的帖子,小S需要找出两个倒排链的交集,且将结果按照从大到小的顺序输出。现在,给定两个单词的倒排链数组 a 和 b,请你帮助小S找出同时包含这两个单词的帖子ID,并按从大到小的顺序返回结果。


测试样例

样例1:

输入:a = [1, 2, 3, 7], b = [2, 5, 7]
输出:[7, 2]

样例2:

输入:a = [1, 4, 8, 10], b = [2, 4, 8, 10]
输出:[10, 8, 4]

样例3:

输入:a = [3, 5, 9], b = [1, 4, 6]
输出:[]

样例4:

输入:a = [1, 2, 3], b = [1, 2, 3]
输出:[3, 2, 1]

1. 暴力双循环解法

思路

暴力解法直接使用两层循环遍历两个列表 ab,查找交集元素。这种方法在理解和实现上都非常简单,但因为要两两比较,复杂度较高。

代码实现

def solution(a, b):
    result = []
    for i in range(len(a)-1, -1, -1):
        for j in range(len(b)-1, -1, -1):
            if a[i] == b[j]:
                result.append(a[i])
    return result

复杂度分析

  • 时间复杂度:O(n×m),其中 n 和 m 分别是 ab 的长度。这是因为我们要遍历 a 中的每个元素,并与 b 中的每个元素进行比较。
  • 空间复杂度:O(k),其中 k 是交集中元素的数量,用于存储结果。

缺点

ab 较大时,算法性能会明显下降,不适合处理大规模数据。


2. 集合交集法

在第一种暴力解法的基础上,我们可以利用 Python 集合的交集运算来提高效率。集合交集运算可以快速找到两个集合的共有元素,复杂度是线性的,并且这个方法天然去重。

思路

  1. ab 转换为集合。
  2. 使用集合的 & 操作符来计算交集。
  3. 将交集结果转换为降序列表。

代码实现

def solution(a, b):
    result = sorted(set(a) & set(b), reverse=True)
    return result

复杂度分析

  • 时间复杂度:O(n+m+klog⁡k),其中 n 和 m 是转换集合的时间,k 是交集大小,降序排序交集时需要 O(klog⁡k)。
  • 空间复杂度:O(n+m+k),用于存储集合和结果。

优点

相较于暴力双循环法,集合交集方法的效率提升明显。尤其在两个列表规模较大时,集合操作比直接双重遍历要快得多。

缺点

需要排序来实现降序输出,并且使用集合时会丢失原始数据的顺序信息。


3. 双指针法(最优解)

思路

双指针法利用了 ab 本身是有序列表的特性。通过初始化两个指针分别指向 ab 的尾部,逐个比较并同时向前移动指针。这样的做法避免了嵌套循环,使得算法能够在线性时间内完成交集查找。

  1. 将两个指针分别初始化到 ab 的尾部(列表的最大元素)。

  2. 比较指针所指的元素:

    • 如果 a[i] == b[j],说明找到交集元素,将其加入 result 并同时左移两个指针。
    • 如果 a[i] > b[j],说明 a[i] 较大,需要左移 i,以便找到较小的相等元素。
    • 如果 a[i] < b[j],则左移 j
  3. 重复上述过程,直到某个指针超出边界。

  4. 因为是从尾部往前查找,结果天然有序(降序),无需额外排序。

代码实现

def solution(a, b):
    i, j = len(a) - 1, len(b) - 1
    result = []
    
    while i >= 0 and j >= 0:
        if a[i] == b[j]:
            result.append(a[i])
            i -= 1
            j -= 1
        elif a[i] > b[j]:
            i -= 1
        else:
            j -= 1

    return result

复杂度分析

  • 时间复杂度:O(n+m),只需要一次遍历 ab,并且在不使用嵌套循环的情况下找到交集。
  • 空间复杂度:O(k),用于存储交集结果。

优点

双指针法在时间和空间上都更高效,是三种方法中复杂度最低的。因为 ab 是有序的,这个方法在找到交集的同时保证了结果的降序排列。

总结

对于初学者来说,在设计算法时很难直接想到复杂度低的解法。不妨仍从暴力算法开始,逐步学习算法优化,用循序渐进的过程培养优化意识。