1. 问题背景与核心需求
这道题目来自AcWing题库第800题,属于双指针算法的经典应用场景。给定两个升序排列的数组A和B,以及一个目标值x,要求找出满足A[i] + B[j] = x的所有元素对(i,j)。这是算法学习中非常典型的"有序数组求和"问题,在各大技术公司的面试题库中出现频率极高。
实际工程中类似场景:电商平台需要匹配用户浏览记录和商品库时,社交软件的好友推荐系统,都需要处理这种双数组匹配问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法选择与复杂度分析
2.1 暴力解法及其缺陷
最直观的解法是双重循环遍历:
cpp复制for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (A[i] + B[j] == x)
return {i, j};
时间复杂度O(n²),当n=1e5时,操作次数会达到1e10量级,显然不可接受。
2.2 双指针算法的优化原理
利用数组有序的特性,我们可以设置两个指针:
- i初始指向A数组开头(最小值)
- j初始指向B数组末尾(最大值)
通过比较A[i]+B[j]与x的大小关系,动态调整指针位置:
- 当A[i]+B[j] > x时,j--(减小总和)
- 当A[i]+B[j] < x时,i++(增大总和)
- 当相等时即为解
2.3 时间复杂度证明
每个指针最多移动n+m次,因此时间复杂度是严格的O(n+m)。对于1e5规模的数据,操作次数约2e5,完全在可接受范围。
3. 完整代码实现与注释
cpp复制#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int A[N], B[N];
int main() {
int n, m, x;
cin >> n >> m >> x;
for (int i = 0; i < n; i++) scanf("%d", &A[i]);
for (int i = 0; i < m; i++) scanf("%d", &B[i]);
for (int i = 0, j = m - 1; i < n; i++) {
while (j >= 0 && A[i] + B[j] > x) j--;
if (A[i] + B[j] == x) {
printf("%d %d\n", i, j);
break;
}
}
return 0;
}
关键点说明:
- 数组大小N设置为1e5+10是为了防止边界溢出
- 使用scanf读取而非cin,大数据量时效率更高
- j指针的移动采用while循环而非if,确保能跳过所有不满足条件的值
4. 边界条件与测试用例
4.1 必须考虑的边界情况
- 两个数组都只有一个元素
- 目标值x比所有元素和小或大
- 存在多个解的情况(题目通常保证唯一解)
- 数组元素可能为负数
4.2 推荐测试用例
text复制// 常规情况
3 4 6
1 2 3
2 3 4 5
// 输出应为 1 2
// 边界情况1
1 1 2
1
1
// 输出应为 0 0
// 边界情况2
2 2 0
-1 0
-1 1
// 输出应为 1 1
5. 算法扩展与变种问题
5.1 三数之和问题
LeetCode 15题,需要找出数组中三个数使它们的和为0。解法思路类似,需要固定一个数后转化为双数之和问题。
5.2 最近接目标的三数之和
LeetCode 16题,找出三个数使它们的和最接近目标值。需要在双指针移动时记录最小差值。
5.3 四数之和问题
LeetCode 18题,通过双重循环外层+双指针内层的方式解决,时间复杂度O(n³)。
6. 工程实践中的优化技巧
- 输入输出优化:当数据量超过1e5时,建议使用快速IO:
cpp复制ios::sync_with_stdio(false);
cin.tie(0);
-
提前终止条件:如果题目允许,可以在找到第一个解后立即终止程序。
-
内存访问优化:将频繁访问的数组声明为全局变量(如本例中的A[N],B[N]),避免栈溢出。
-
多语言实现对比:Python版本虽然简洁,但处理1e5数据时可能会超时,建议用PyPy解释器运行:
python复制n, m, x = map(int, input().split())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
i, j = 0, m - 1
while i < n and j >= 0:
if A[i] + B[j] == x:
print(i, j)
break
elif A[i] + B[j] < x:
i += 1
else:
j -= 1
7. 常见错误与调试技巧
7.1 指针越界问题
忘记检查j>=0条件会导致数组访问越界。建议在while循环中始终先检查边界条件。
7.2 死循环风险
如果忘记移动指针会导致无限循环。可以在调试时添加打印语句:
cpp复制cout << "i=" << i << " j=" << j << endl; // 调试用
7.3 数据范围错误
没有正确估计数组大小会导致运行时错误。建议:
- 仔细阅读题目给出的数据范围
- 全局数组多开10-20个元素作为缓冲
8. 同类题目推荐
- LeetCode 167. 两数之和 II - 输入有序数组
- LeetCode 653. 两数之和 IV - 输入BST
- AcWing 2816. 判断子序列(双指针判断子序列)
- AcWing 800. 数组元素的目标和(本题)
- AcWing 802. 区间和(前缀和+双指针)
对于想系统学习双指针算法的同学,建议按照"简单->中等->困难"的顺序刷题,逐步掌握滑动窗口、快慢指针等变种技巧。
