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

96 阅读4分钟

小S的倒排索引问题描述

问题背景

在信息检索系统中,倒排索引(Inverted Index)是用于高效检索的一种数据结构。在搜索引擎中,倒排索引的目的是将文档中的每个单词映射到包含该单词的文档ID。这使得当用户查询一个或多个关键词时,能够迅速找到包含这些关键词的文档。而在实际应用中,倒排索引通常用于处理大量文档和查询。

小S正在帮助她的朋友们建立一个搜索引擎,并且她决定使用倒排索引来提升搜索速度。她的任务是为搜索引擎实现一个功能,能够查询多个单词的倒排索引,并找到同时包含这些单词的文档ID。

问题描述

给定两个单词的倒排索引列表 ab,我们需要找出同时包含这两个单词的文档ID,并按从大到小的顺序返回结果。

输入

  • 两个已排序的数组 ab,分别表示单词A和B的倒排索引。

输出

  • 一个按从大到小顺序排列的数组,表示同时包含两个单词的文档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

输入

css
复制代码
a = [3, 5, 9]
b = [1, 4, 6]

输出

[]
示例 4

输入

a = [1, 2, 3]
b = [1, 2, 3]

输出

[3, 2, 1]

解决思路

1. 基本思路

倒排索引问题的核心是求两个有序数组的交集。我们需要在两个已排序的数组 ab 中找到相同的元素,并将结果按从大到小的顺序返回。

步骤
  1. 使用双指针法:由于数组 ab 已经排序,我们可以使用双指针方法来高效地求解交集。具体地,指针 i 用于遍历数组 a,指针 j 用于遍历数组 b
  2. 比较元素:通过比较 a[i]b[j],如果两个元素相等,则将它们加入结果列表,并移动两个指针。如果 a[i] < b[j],则移动指针 i;如果 a[i] > b[j],则移动指针 j
  3. 返回结果:由于我们需要按从大到小的顺序返回结果,因此可以在找到交集时将结果插入到一个列表中,并在最后反转该列表。
复杂度分析
  • 时间复杂度:由于我们使用双指针法遍历两个已排序数组,因此时间复杂度为 O(n + m),其中 nm 分别为数组 ab 的长度。
  • 空间复杂度:我们只需要额外的空间来存储交集结果,因此空间复杂度为 O(min(n, m))。

2. 代码实现

#include <iostream>
#include <vector>
using namespace std;

vector<int> solution(vector<int>& a, vector<int>& b) {
    vector<int> result;
    int i = a.size() - 1, j = b.size() - 1;

    // 双指针遍历两个数组
    while (i >= 0 && j >= 0) {
        if (a[i] == b[j]) {
            result.push_back(a[i]);
            i--;
            j--;
        } else if (a[i] > b[j]) {
            i--;
        } else {
            j--;
        }
    }

    // 结果反转(从大到小排序)
    reverse(result.begin(), result.end());
    return result;
}

int main() {
    vector<int> a1 = {1, 2, 3, 7};
    vector<int> b1 = {2, 5, 7};
    vector<int> res1 = solution(a1, b1);
    cout << (res1 == vector<int>{7, 2}) << endl;

    vector<int> a2 = {1, 4, 8, 10};
    vector<int> b2 = {2, 4, 8, 10};
    vector<int> res2 = solution(a2, b2);
    cout << (res2 == vector<int>{10, 8, 4}) << endl;

    vector<int> a3 = {3, 5, 9};
    vector<int> b3 = {1, 4, 6};
    vector<int> res3 = solution(a3, b3);
    cout << (res3 == vector<int>{}) << endl;

    vector<int> a4 = {1, 2, 3};
    vector<int> b4 = {1, 2, 3};
    vector<int> res4 = solution(a4, b4);
    cout << (res4 == vector<int>{3, 2, 1}) << endl;

    return 0;
}

3. 代码解析

  • 我们首先声明了一个空的 result 向量来存储交集的结果。
  • 使用两个指针 ij 分别从 ab 的末尾开始遍历。
  • 在每一步,我们比较 a[i]b[j],如果相等,将其加入结果中,并且将两个指针分别向前移动;如果不相等,则将较大的指针向前移动。
  • 最后,结果数组需要反转,确保按从大到小的顺序返回。

4. 思考与优化

双指针法的优势
  • 效率高:由于输入数组已经排序,双指针方法能够以 O(n + m) 的时间复杂度解决问题,比暴力解法要高效得多。
  • 内存节省:我们只需一个结果数组来存储交集,因此空间复杂度为 O(min(n, m)),避免了不必要的额外空间开销。
扩展

如果数组 ab 的长度非常大,且查询的次数很多,我们可以考虑将倒排索引结构做进一步优化,例如使用哈希表或者平衡树来存储倒排索引,以加速查找过程。

总结

本问题涉及到倒排索引和交集的查询,是信息检索和搜索引擎中的经典问题。通过双指针法,我们能高效地找到两个排序数组的交集,并按要求返回结果。随着数据量的增加,算法的效率和空间复杂度将变得尤为重要,合理的优化方案能够显著提高程序的性能。