题目描述
给定一个已经按照升序排列的有序数组,找到两个数使得他们相加之和等于目标数。
函数应该返回这两个下标值index1和index2,其中index1必须小于index2。
说明:
- 返回的下标值(index1和index2)不是从零开始的。
- 你可以假设每个输入只对应唯一的答案,而且你不可以重复的使用相同的元素。
示例:
- 输入:numbers = [2, 7, 11, 15], target = 9
- 输出:[1, 2]
- 解析:2与7之和等于目标数9。因此index1 = 1,index2 = 2。
解法:
- 由于题目给出的数组是有序的了,所以可以使用二分法进行查找
- 注意:题目要求返回的下标不是从0开始的,那么就是从1开始的了。
class Solution:
# 变量后面的是类型的定义如:List[int],int等,返回值类型rtype:List[int]
def twoSum(self, numbers: List[int], target: int) -> List[int]:
# 数组有序之后可以使用二分法查找
size = len(numbers)
for i in range(size-1):
# 初始化左指针和右指针 题目要求不能返回下标为0的,也就是第一个
left = i+1
right = size - 1
while left <= right:
mid = (left+right)//2
if numbers[i] + numbers[mid] < target:
left = mid + 1
elif numbers[i] + numbers[mid] > target:
right = mid-1
else:
# 返回的值从非0开始:
return [i+1,mid+1]
- 官方给出的C++解法:
- 使用双指针
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int low=0,high = numbers.size() - 1;
//如果刚开始两数之和就等于目标值就返回,否则双指针从两头向中间靠拢
while (low < high){
// 这里不需要考虑numbers[low] + numbers[high]溢出,因为即使两个元素之和溢出了,因为只存在唯一解,所以一定会先访问到答案。
int sum = numbers[low] + numbers[high];
if (sum == target)
return {low + 1, high + 1};
else if (sum < target)
++low;
else
--high;
}
// 找不到就返回-1,一定要返回一个东西,因为函数是non-void的
return {-1,-1};
};
};
算法分析:
我们可以使用 两数之和 的解法在 O(n^2)时间 O(1) 空间暴力解决,也可以用哈希表在 O(n)O(n) 时间和 O(n)O(n) 空间内解决。然而,这两种方法都没有用到输入数组已经排序的性质,我们可以做得更好。
我们使用两个指针,初始分别位于第一个元素和最后一个元素位置,比较这两个元素之和与目标值的大小。如果和等于目标值,我们发现了这个唯一解。如果比目标值小,我们将较小元素指针增加一。如果比目标值大,我们将较大指针减小一。移动指针后重复上述比较知道找到答案。
假设 [… , a, b, c, … , d, e, f, …][…,a,b,c,…,d,e,f,…] 是已经升序排列的输入数组,并且元素 b, eb,e 是唯一解。因为我们从左到右移动较小指针,从右到左移动较大指针,总有某个时刻存在一个指针移动到 bb 或 ee 的位置。不妨假设小指针县移动到了元素 bb ,这是两个元素的和一定比目标值大,根据我们的算法,我们会向左移动较大指针直至获得结果。
复杂度分析:
- 时间复杂度:O(n)。每个元素最多被访问一次,共有n个元素。
- 空间复杂度:O(1)。只是用了两个指针。