小S的倒排索引 | 豆包MarsCode AI刷题

137 阅读3分钟

题目描述

image.png

分析

给定两个已排序的列表,直接用集合求交集是一个自然的选择。集合的交集操作非常高效,可以在常数时间内判断元素是否在集合中。然而,计算交集后,我们还需要将结果按从大到小的顺序排列。排序操作会占用一定的时间,特别是当交集元素较多时,效率可能会受到影响。

解法一

  • 转换为集合:由于集合的查找和插入操作是 O(1) 的,我们可以把列表 ab 转换为集合,快速找出它们的交集。
  • 求交集:使用 set_a & set_b 操作,找到交集元素。
  • 排序:使用 sorted 函数将交集结果按从大到小排序。

代码实现如下:

def solution(a, b):
    # 将列表转换为集合
    set_a = set(a)
    set_b = set(b)
    
    # 求交集
    intersection = set_a & set_b
    
    # 排序交集,按从大到小的顺序
    result = sorted(intersection, reverse=True)
    
    return result

这段代码的核心在于集合的交集操作,时间复杂度为 O(min(len(a), len(b)))。但是排序操作的时间复杂度是 O(n log n),其中 n 是交集元素的数量。如果交集元素较少,这种方法的效率已经非常高了。

解法二

虽然集合操作本身很高效,但我们可以进一步优化代码,减少不必要的计算。

由于输入列表已经是有序的,可以利用双指针法在不转换为集合的情况下直接求交集。双指针法的优势在于,它可以在线性时间内求解交集,并且不需要额外的空间来存储集合。

具体做法如下:

  • 初始化两个指针,分别指向列表 ab 的开头。

  • 比较两个指针所指向的元素:

    • 如果相等,将该元素加入交集,两个指针都向后移动。
    • 如果 a[i] < b[j],则 a 的指针向后移动。
    • 如果 a[i] > b[j],则 b 的指针向后移动。
  • 最终得到交集,直接进行排序。

这种方法的时间复杂度是 O(len(a) + len(b)),没有额外的空间开销。

代码实现:

def solution(a, b):
    i, j = 0, 0
    intersection = []
    
    # 双指针法求交集
    while i < len(a) and j < len(b):
        if a[i] == b[j]:
            intersection.append(a[i])
            i += 1
            j += 1
        elif a[i] < b[j]:
            i += 1
        else:
            j += 1
    
    # 排序交集,从大到小
    return sorted(intersection, reverse=True)

对比分析

  • 集合法:通过集合求交集,转换集合的时间复杂度是 O(len(a) + len(b)),交集操作是 O(min(len(a), len(b))),然后排序的时间复杂度是 O(n log n)。整体时间复杂度为 O(min(len(a), len(b)) + n log n),其中 n 是交集的元素数量。
  • 双指针法:双指针法的时间复杂度是 O(len(a) + len(b)),不需要转换为集合,也不需要额外的内存开销,适合大数据量时使用。

总结

对于本问题,最常见的做法是先将列表转换为集合,然后求交集并排序。这种方法简单且有效,但如果输入数据量很大,双指针法可能会更高效,因为它避免了集合的额外转换和内存消耗。

  • 当交集元素较少时,使用集合法更为简单。
  • 当交集较大或数据量较大时,双指针法更为高效,尤其是在不需要额外空间的情况下。

但是暴力法能过,所以……