题目理解:
题目要求我们在给定的两个列表中找到它们的交集,并且返回结果时需要按照从 大到小 的顺序排序。这两个列表分别表示两个单词的倒排索引,每个列表中包含的是一个帖子ID,且这些帖子ID已经按 从小到大的顺序 排列。
关键概念:
- 倒排索引:
- 每个单词都对应一个帖子ID列表,这些帖子ID表示该单词在哪些帖子中出现过。
- 例如,如果“夏天”出现在帖子1、帖子3和帖子7中,那么“夏天”的倒排索引就是
[1, 3, 7]。
- 交集查询:
- 用户希望能够查询同时包含某两个单词的帖子。这就要求我们计算两个倒排索引的交集。交集即同时出现在两个列表中的帖子ID。
- 从大到小排序:
- 交集结果需要按从大到小的顺序返回,而原始的两个列表是按从小到大的顺序排列的。
解题思路:
- 双指针法:因为两个列表已经是有序的,所以可以使用双指针法高效地找出交集。具体步骤如下:
- 设置两个指针分别指向两个列表的开头。
- 比较当前指针所指的元素:
- 如果相等,则将该元素添加到结果列表,并同时移动两个指针。
- 如果不相等,则移动指向较小元素的指针,继续比较。
- 这样可以在 O(n + m) 的时间复杂度内求得交集,其中
n和m分别是两个列表的长度。
- 反转结果:交集得到后,直接反转结果列表,以保证按照从大到小的顺序返回。
代码实现:
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)));
}
}
代码解析:
-
初始化指针:
i = 0和j = 0分别是列表a和b的指针,指向列表的开头。
-
双指针遍历:
-
使用
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。
- 如果
-
-
反转结果:
- 因为交集是按照从小到大的顺序得到的,所以我们需要使用
Collections.reverse(result)将结果列表反转,得到从大到小的顺序。
- 因为交集是按照从小到大的顺序得到的,所以我们需要使用
时间复杂度:
- 双指针遍历:时间复杂度为 O(n + m),其中
n和m分别是列表a和b的长度。 - 反转结果:反转操作的时间复杂度为 O(k),其中
k是交集的大小。因此,整体时间复杂度为 O(n + m + k)。
空间复杂度:
- 使用了一个额外的
List<Integer>存储交集结果,因此空间复杂度为 O(k),其中k是交集的大小。
测试用例:
-
测试1:
solution(Arrays.asList(1, 2, 3, 7), Arrays.asList(2, 5, 7));输出:
[7, 2]- 交集为
[2, 7],反转后得到[7, 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:
solution(Arrays.asList(3, 5, 9), Arrays.asList(1, 4, 6));输出:
[]- 没有交集,返回空列表。
-
测试4:
solution(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3));输出:
[3, 2, 1]- 完全相同的交集,反转后得到
[3, 2, 1]。
- 完全相同的交集,反转后得到
总结:
- 双指针法是解决这种有序列表交集问题的高效方法,能够避免暴力的 O(n * m) 时间复杂度。
- 反转结果是根据题目要求的排序方式进行的。
- 这种解法既高效又简洁,时间和空间复杂度都得到了优化。