给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
- 返回的下标值(index1 和 index2)不是从零开始的。
- 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
在这里想和大家分享一下基本的解题思路,诚然这道题目非常简单,但是当遇到难题的时候,一个好的解题思路或许会为我们找到突破点。这道题用暴力解法,两次循环便可得出答案,但是提交给leetcode应该会timeout,因为O(n2)基本的算法在数据量太大的时候,会非常耗时,那么怎么降低时间复杂度呢,结合题目,我们可以看到数组是有序,既然是有序的数组,是否可以用二分查找来寻找target-number[i]呢?
public int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
for (int i = 0; i < numbers.length; i++) {
int pos = findIndex(numbers, i + 1, numbers.length - 1, target - numbers[i]);
if (pos >= 0) {
res[0] = i + 1;
res[1] = pos + 1;
}
}
return res;
}
private int findIndex(int[] numbers, int start, int end, int target) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (numbers[mid] == target) {
return mid;
} else if (numbers[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return -1;
}
这样降到O(nlogn)便可以AC了,那么还有更好的解决方案吗,在遍历一次的情况下,便可以得到结果,这里可以使用对撞指针来解决,分别使两个指针指向首尾,如果相加等于,便找到了对应的索引,如果小于需要右移左指针,大于便左移右指针。
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (l < r) {
if (numbers[l] + numbers[r] == target) {
return new int[]{l + 1, r + 1};
} else if (numbers[l] + numbers[r] < target) {
l++;
} else {
r--;
}
}
return new int[2];
}
下面是Python实现
class Solution:
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
i = 0
j = len(numbers) - 1
while i < j:
if numbers[i] + numbers[j] == target:
return [i + 1, j + 1]
elif numbers[i] + numbers[j] < target:
i += 1
else:
j -= 1
关于对撞指针,类似的题目还有Leetcode125号问题:验证回文串, 345号问题 反转字符串中的元音字母等,有兴趣的同学也可以做做这些题目。