数组元素的目标和
给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。数组下标从 0 开始。请你求出满足 A[i] + B[j] = x 的数对 (i, j)。数据保证有唯一解。
所用方法和基本原理
- 双指针法原理:
- 该代码运用双指针法来解决问题。双指针法是一种常用的算法技巧,通过使用两个指针遍历数据结构,根据特定条件移动指针以优化遍历过程。
- 这里设置两个指针,一个指针
i指向数组arr1的起始位置(即i = 0),另一个指针j指向数组arr2的末尾位置(即j = m - 1,其中m是数组arr2的长度)。 - 由于数组
arr1和arr2都是升序排列的,通过比较arr1[i] + arr2[j]与目标值x的大小关系来移动指针。 - 如果
arr1[i] + arr2[j] > x,说明当前两数之和过大,因为arr2是升序的,所以需要将j指针向左移动(即j--),使得arr2[j]变小,从而减小两数之和。 - 如果
arr1[i] + arr2[j] < x,说明当前两数之和过小,由于arr1是升序的,所以需要将i指针向右移动(在外部的for循环中实现),使得arr1[i]变大,进而增大两数之和。 - 当
arr1[i] + arr2[j] == x时,就找到了满足条件的数对(i, j),直接返回该数对。
代码及注释
public class TwoSumForSortedArrays {
// 寻找满足 arr1[i] + arr2[j] = x 的数对 (i, j)
public static int[] twoSum(int[] arr1, int[] arr2, int x) {
int n = arr1.length;
int m = arr2.length;
// i 指针遍历 arr1,j 指针从 arr2 的末尾开始
for (int i = 0, j = m - 1; i < n; i++) {
// 当 arr1[i] + arr2[j] > x 时,减小 j 以减小两数之和
while (j >= 0 && arr1[i] + arr2[j] > x) j--;
// 如果找到满足条件的数对,返回结果
if (arr1[i] + arr2[j] == x) return new int[]{i, j};
}
// 如果未找到,返回 {-1, -1}
return new int[]{-1, -1};
}
}
举例说明
假设 arr1 = [1, 3, 5, 7],arr2 = [2, 4, 6, 8],目标值 x = 9。
- 初始化:
n = 4,m = 4,i = 0,j = 3。 - 第一轮循环:
i = 0,arr1[0] = 1,j = 3,arr2[3] = 8,arr1[0] + arr2[3] = 1 + 8 = 9。- 因为
arr1[0] + arr2[3] == 9,直接返回[0, 3]。
再假设 arr1 = [2, 4, 6],arr2 = [3, 5, 7],目标值 x = 10。
- 初始化:
n = 3,m = 3,i = 0,j = 2。 - 第一轮循环:
i = 0,arr1[0] = 2,j = 2,arr2[2] = 7,arr1[0] + arr2[2] = 2 + 7 = 9。- 因为
arr1[0] + arr2[2] < 10,i在外部for循环中自增,i变为1。
- 第二轮循环:
i = 1,arr1[1] = 4,j = 2,arr2[2] = 7,arr1[1] + arr2[2] = 4 + 7 = 11。- 因为
arr1[1] + arr2[2] > 10,进入while循环,j--,j变为1。 - 此时
arr1[1] + arr2[1] = 4 + 5 = 9,继续while循环,j--,j变为0。 - 此时
arr1[1] + arr2[0] = 4 + 3 = 7,while循环结束。 - 因为
arr1[1] + arr2[0] < 10,i在外部for循环中自增,i变为2。
- 第三轮循环:
i = 2,arr1[2] = 6,j = 0,arr2[0] = 3,arr1[2] + arr2[0] = 6 + 3 = 9。- 因为
arr1[2] + arr2[0] < 10,i在外部for循环中自增,但i已达到数组arr1的末尾,循环结束。 - 未找到满足条件的数对,返回
[-1, -1]。
方法的优劣
- 时间复杂度:
- 外层
for循环遍历数组arr1一次,时间复杂度为 (O(n)),其中n是数组arr1的长度。 - 内层
while循环在每次外层循环时,最多执行m次(m是数组arr2的长度),但整体来看,指针j最多从数组arr2的末尾移动到开头一次。所以内层while循环的总时间复杂度为 (O(m))。 - 总体时间复杂度为 (O(n + m)),这里
n和m分别是两个数组的长度。相比于暴力解法(时间复杂度为 (O(n \times m)),需要嵌套遍历两个数组),双指针法效率更高。
- 外层
- 空间复杂度:
- 除了输入的数组外,只使用了常数级别的额外空间(如
n,m,i,j等变量),空间复杂度为 (O(1))。
- 除了输入的数组外,只使用了常数级别的额外空间(如
优点: - 时间复杂度低,通过双指针法避免了嵌套循环带来的高时间复杂度,能高效地解决有序数组的两数之和问题。 - 空间复杂度小,只需要常数级别的额外空间,对内存友好。
缺点: - 该方法依赖于数组的有序性,如果数组无序,需要先进行排序,而排序操作可能会增加额外的时间复杂度(如使用快速排序,时间复杂度为 (O(n \log n)))。 - 只能处理两数之和的问题,对于更复杂的多数之和问题,需要对算法进行大幅修改或采用其他方法。