题目解析: 小S的倒排索引 | 豆包MarsCode AI刷题

75 阅读5分钟

解题思路

题目要求我们找到两个倒排索引列表的交集,并且将结果按降序排列输出。这里的两个倒排链其实就是两个排序好的整数数组,表示含有特定关键词的帖子ID。我们的目标是找到同时包含这两个关键词的所有帖子ID,并且按照从大到小的顺序返回。

1. 理解倒排索引的特点

倒排索引本质上是一个单词到包含该单词的文档ID(帖子ID)之间的映射。每个关键词都会对应一个按升序排列的帖子ID列表。比如,假设我们有以下两个关键词的倒排链:

  • 关键词A的倒排链:[1, 2, 3, 7]
  • 关键词B的倒排链:[2, 5, 7]

我们的任务就是找出这两个列表的交集,即同时包含关键词A和关键词B的帖子ID,最后返回的结果是[7, 2](从大到小排序)。

2. 交集问题

这道题目本质上是一个典型的两个有序数组的交集问题。由于输入的两个列表是已经排好序的,我们可以利用这个有序性,采用双指针的方式高效地求解交集。

3. 双指针法

双指针是一种在两个有序数组中寻找交集的经典方法。通过设置两个指针分别指向两个数组的起始位置,我们逐步比较数组中的元素,并根据比较结果来移动指针,直到遍历完数组为止。具体步骤如下:

  • 初始化两个指针ij,分别指向数组a和数组b的开始位置。
  • 如果a[i] == b[j],说明a[i]b[j]是相同的帖子ID,因此我们可以将它加入结果列表中,并同时移动两个指针i++j++
  • 如果a[i] < b[j],说明a[i]b[j]小,由于两个数组是升序排列的,所以a[i]不可能出现在b[j]后面,因此我们需要将指针i向前移动,即i++
  • 如果a[i] > b[j],说明a[i]b[j]大,同样,由于两个数组是升序排列的,b[j]不可能出现在a[i]后面,因此我们需要将指针j向前移动,即j++

通过这种方式,我们可以高效地找到两个数组的交集。

4. 降序排序

在找到交集后,题目要求我们返回的结果需要按降序排列。因为两个输入数组是升序排列的,而交集的部分自然也会是升序排列的,因此只需要在返回结果前,调用Collections.reverse(result)将结果逆序即可。

5. 总结

  • 双指针法:通过双指针遍历两个已排序的数组,找到交集部分。
  • 逆序输出:由于题目要求按降序排列结果,所以找到交集后需要进行逆序处理。

具体步骤

  1. 初始化指针:我们分别用ij指向两个数组ab的开始位置。

  2. 逐步比较:通过比较a[i]b[j]的值:

    • 如果相等,将该值加入结果数组,并同时移动两个指针。
    • 如果a[i] < b[j],说明a[i]不在b中,指针i向后移动。
    • 如果a[i] > b[j],说明b[j]不在a中,指针j向后移动。
  3. 处理结束:当我们遍历完两个数组后,结果数组即为交集,按升序排列。我们使用Collections.reverse(result)来将结果数组逆序,得到降序排列的结果。

代码实现

下面是基于上述思路的代码实现:

import java.util.*;

public class Main {
    public static List<Integer> solution(List<Integer> a, List<Integer> b) {
        List<Integer> result = new ArrayList<>();
        int i = 0, j = 0;
        int sizeA = a.size();
        int sizeB = b.size();
        
        // 双指针遍历两个数组
        while (i < sizeA && j < sizeB) {
            int valA = a.get(i);
            int valB = b.get(j);
            
            if (valA == valB) {
                // 找到相同的帖子ID,加入结果并同时移动两个指针
                result.add(valA);
                i++;
                j++;
            } else if (valA < valB) {
                // 如果a[i]小于b[j],说明a[i]不在b中,指针i向后移动
                i++;
            } else {
                // 如果a[i]大于b[j],说明b[j]不在a中,指针j向后移动
                j++;
            }
        }
        
        // 逆序结果列表,得到降序排列
        Collections.reverse(result);
        return result;
    }

    public static void main(String[] args) {
        System.out.println(solution(Arrays.asList(1, 2, 3, 7), Arrays.asList(2, 5, 7)).equals(Arrays.asList(7, 2)));
        System.out.println(solution(Arrays.asList(1, 4, 8, 10), Arrays.asList(2, 4, 8, 10)).equals(Arrays.asList(10, 8, 4)));
        System.out.println(solution(Arrays.asList(3, 5, 9), Arrays.asList(1, 4, 6)).equals(Collections.emptyList()));
        System.out.println(solution(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3)).equals(Arrays.asList(3, 2, 1)));
    }
}

复杂度分析

  • 时间复杂度: O(n + m),其中nm分别是数组ab的长度。由于我们通过双指针一次遍历两个数组,因此时间复杂度是线性的。
  • 空间复杂度: O(k),其中k是两个数组的交集大小。我们需要额外的空间存储交集结果。最坏情况下,两个数组完全相同,结果列表的大小为n(或m),所以空间复杂度为O(n)

总结

这道题目利用了倒排索引的基本概念,并通过双指针法高效地找出了两个排序数组的交集。由于输入的数组是排序的,我们可以通过双指针避免了不必要的遍历,降低了时间复杂度。此外,通过简单的逆序操作,我们也能满足题目对于输出顺序的要求。这种解法非常适合处理大规模数据集,且易于理解和实现。