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

46 阅读4分钟

题目理解:

题目要求我们在给定的两个列表中找到它们的交集,并且返回结果时需要按照从 大到小 的顺序排序。这两个列表分别表示两个单词的倒排索引,每个列表中包含的是一个帖子ID,且这些帖子ID已经按 从小到大的顺序 排列。

关键概念:

  1. 倒排索引
    • 每个单词都对应一个帖子ID列表,这些帖子ID表示该单词在哪些帖子中出现过。
    • 例如,如果“夏天”出现在帖子1、帖子3和帖子7中,那么“夏天”的倒排索引就是 [1, 3, 7]
  2. 交集查询
    • 用户希望能够查询同时包含某两个单词的帖子。这就要求我们计算两个倒排索引的交集。交集即同时出现在两个列表中的帖子ID。
  3. 从大到小排序
    • 交集结果需要按从大到小的顺序返回,而原始的两个列表是按从小到大的顺序排列的。

解题思路:

  1. 双指针法:因为两个列表已经是有序的,所以可以使用双指针法高效地找出交集。具体步骤如下:
    • 设置两个指针分别指向两个列表的开头。
    • 比较当前指针所指的元素:
      • 如果相等,则将该元素添加到结果列表,并同时移动两个指针。
      • 如果不相等,则移动指向较小元素的指针,继续比较。
    • 这样可以在 O(n + m) 的时间复杂度内求得交集,其中 nm 分别是两个列表的长度。
  2. 反转结果:交集得到后,直接反转结果列表,以保证按照从大到小的顺序返回。

代码实现:

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;
        
        // 使用双指针遍历两个列表
        while (i < a.size() && j < b.size()) {
            if (a.get(i).equals(b.get(j))) {
                result.add(a.get(i)); // 找到相同的ID,添加到结果中
                i++;
                j++;
            } else if (a.get(i) < b.get(j)) {
                i++; // a[i] 小于 b[j],所以移动 i
            } else {
                j++; // b[j] 小于 a[i],所以移动 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)));
    }
}

代码解析:

  1. 初始化指针

    • i = 0j = 0 分别是列表 ab 的指针,指向列表的开头。
  2. 双指针遍历

    • 使用

      while
      

      循环,当两个指针都没有超出各自列表的范围时,进行循环比较:

      • 如果 a.get(i)b.get(j) 相等,说明找到了一个交集元素,将其添加到结果列表,并分别移动两个指针。
      • 如果 a.get(i) 小于 b.get(j),说明 a.get(i) 不在交集中,移动指针 i
      • 如果 a.get(i) 大于 b.get(j),说明 b.get(j) 不在交集中,移动指针 j
  3. 反转结果

    • 因为交集是按照从小到大的顺序得到的,所以我们需要使用 Collections.reverse(result) 将结果列表反转,得到从大到小的顺序。

时间复杂度:

  • 双指针遍历:时间复杂度为 O(n + m),其中 nm 分别是列表 ab 的长度。
  • 反转结果:反转操作的时间复杂度为 O(k),其中 k 是交集的大小。因此,整体时间复杂度为 O(n + m + k)。

空间复杂度:

  • 使用了一个额外的 List<Integer> 存储交集结果,因此空间复杂度为 O(k),其中 k 是交集的大小。

测试用例:

  1. 测试1

    solution(Arrays.asList(1, 2, 3, 7), Arrays.asList(2, 5, 7));
    

    输出:[7, 2]

    • 交集为 [2, 7],反转后得到 [7, 2]
  2. 测试2

    solution(Arrays.asList(1, 4, 8, 10), Arrays.asList(2, 4, 8, 10));
    

    输出:[10, 8, 4]

    • 交集为 [4, 8, 10],反转后得到 [10, 8, 4]
  3. 测试3

    solution(Arrays.asList(3, 5, 9), Arrays.asList(1, 4, 6));
    

    输出:[]

    • 没有交集,返回空列表。
  4. 测试4

    solution(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3));
    

    输出:[3, 2, 1]

    • 完全相同的交集,反转后得到 [3, 2, 1]

总结:

  • 双指针法是解决这种有序列表交集问题的高效方法,能够避免暴力的 O(n * m) 时间复杂度。
  • 反转结果是根据题目要求的排序方式进行的。
  • 这种解法既高效又简洁,时间和空间复杂度都得到了优化。