哈希表与二分查找优化二元组统计问题

发布时间:2026/11/4 21:06:19
哈希表与二分查找优化二元组统计问题

1. 题目背景与核心需求

这道来自蚂蚁集团2026年春招的编程题,考察的是对二元组数据的处理能力。题目描述如下:

给定两个整数数组nums1和nums2,统计满足以下条件的二元组(i,j)的数量:

  1. nums1[i] == nums2[j]
  2. i < j

这类问题在实际开发中非常常见,比如在电商系统中匹配用户行为日志、在推荐系统中对齐用户画像特征等。理解这类问题的解法,对处理真实业务中的数据集对齐场景很有帮助。

2. 暴力解法与复杂度分析

最直观的解法是双层循环遍历:

def count_pairs(nums1, nums2): count = 0 for i in range(len(nums1)): for j in range(len(nums2)): if nums1[i] == nums2[j] and i < j: count += 1 return count

时间复杂度:O(n²)
空间复杂度:O(1)

虽然这种解法在小数据量时可行,但在面试中显然不够理想。蚂蚁的面试题通常期望候选人能给出更优解。

3. 哈希表优化解法

我们可以利用哈希表来优化时间复杂度:

from collections import defaultdict def count_pairs(nums1, nums2): num_map = defaultdict(list) for idx, num in enumerate(nums2): num_map[num].append(idx) count = 0 for i, num in enumerate(nums1): if num in num_map: # 找到nums2中所有等于nums1[i]且位置大于i的j for j in num_map[num]: if i < j: count += 1 return count

时间复杂度:O(n+m),其中n和m分别是两个数组的长度
空间复杂度:O(m),用于存储nums2的索引

4. 进一步优化:二分查找

当nums2中存在大量重复元素时,可以进一步优化:

import bisect def count_pairs(nums1, nums2): num_map = defaultdict(list) for idx, num in enumerate(nums2): num_map[num].append(idx) count = 0 for i, num in enumerate(nums1): if num in num_map: # 使用二分查找找到第一个大于i的位置 indices = num_map[num] pos = bisect.bisect_right(indices, i) count += len(indices) - pos return count

时间复杂度:O(n log m)
空间复杂度:O(m)

5. 多语言实现对比

5.1 Java实现

import java.util.*; public class Solution { public int countPairs(int[] nums1, int[] nums2) { Map<Integer, List<Integer>> numMap = new HashMap<>(); for (int j = 0; j < nums2.length; j++) { numMap.computeIfAbsent(nums2[j], k -> new ArrayList<>()).add(j); } int count = 0; for (int i = 0; i < nums1.length; i++) { if (numMap.containsKey(nums1[i])) { List<Integer> indices = numMap.get(nums1[i]); int pos = Collections.binarySearch(indices, i); if (pos < 0) pos = -pos - 1; count += indices.size() - pos; } } return count; } }

5.2 C++实现

#include <vector> #include <unordered_map> #include <algorithm> using namespace std; int countPairs(vector<int>& nums1, vector<int>& nums2) { unordered_map<int, vector<int>> numMap; for (int j = 0; j < nums2.size(); ++j) { numMap[nums2[j]].push_back(j); } int count = 0; for (int i = 0; i < nums1.size(); ++i) { if (numMap.find(nums1[i]) != numMap.end()) { auto& indices = numMap[nums1[i]]; auto it = upper_bound(indices.begin(), indices.end(), i); count += distance(it, indices.end()); } } return count; }

5.3 Python实现(最终优化版)

from collections import defaultdict import bisect def count_pairs(nums1, nums2): num_map = defaultdict(list) for idx, num in enumerate(nums2): num_map[num].append(idx) count = 0 for i, num in enumerate(nums1): if num in num_map: indices = num_map[num] pos = bisect.bisect_right(indices, i) count += len(indices) - pos return count

6. 测试用例设计

全面的测试用例应该包括:

test_cases = [ # 基础测试 ([1,2,3], [1,2,3], 3), ([1,1,1], [1,1,1], 6), # 边界测试 ([], [], 0), ([1], [2], 0), # 特殊测试 ([1,2,3], [3,2,1], 1), ([1,2,2], [2,2,2], 4), # 大数据测试 (list(range(10000)), list(range(10000)), 49995000) ] for nums1, nums2, expected in test_cases: assert count_pairs(nums1, nums2) == expected

7. 性能优化思考

在实际业务场景中,还可以考虑以下优化方向:

  1. 如果数组已经排序,可以使用双指针法进一步优化
  2. 对于特别大的数据集,可以考虑分块处理或使用布隆过滤器
  3. 在多线程环境下,可以将哈希表的构建和查询过程并行化

8. 常见面试问题

面试官可能会追问:

  1. 如何处理数据量特别大的情况?
  2. 如果nums2不能全部放入内存怎么办?
  3. 如何将这个算法扩展到分布式环境?

9. 实际业务应用

这类算法在以下场景中有广泛应用:

  1. 用户行为分析:匹配不同时间段的用户行为
  2. 推荐系统:对齐用户和物品的特征
  3. 数据清洗:找出不同数据源中的匹配项

10. 编码规范建议

在面试中写代码时要注意:

  1. 先明确输入输出和边界条件
  2. 写注释说明算法思路
  3. 变量命名要有意义
  4. 处理可能的异常情况

11. 复杂度分析进阶

对于更复杂的情况,可以考虑:

  1. 预处理时间复杂度
  2. 查询时间复杂度
  3. 空间复杂度的权衡
  4. 数据分布对算法性能的影响

12. 算法选择策略

选择算法时需要考虑:

  1. 数据规模
  2. 数据分布特征
  3. 内存限制
  4. 是否需要支持动态更新

13. 在线测试技巧

在在线编程平台测试时:

  1. 先通过所有示例测试用例
  2. 添加自己的边界测试
  3. 测试大数据量下的表现
  4. 检查内存使用情况

14. 面试评分标准

这类题目通常会考察:

  1. 基础编码能力
  2. 算法优化思路
  3. 代码规范性
  4. 问题分析能力
  5. 沟通表达能力

15. 学习资源推荐

想深入掌握这类问题可以学习:

  1. 《算法导论》中的哈希表相关章节
  2. LeetCode上的Two Sum系列题目
  3. 系统设计中的数据处理模式
  4. 分布式算法中的数据分片技术

16. 个人实战建议

根据我的面试经验:

  1. 先写出暴力解法,再逐步优化
  2. 主动分析时间/空间复杂度
  3. 讨论可能的优化方向
  4. 考虑实际业务中的约束条件
  5. 保持代码整洁和可读性