167. 两数之和 II - 输入有序数组
给定一个已按照非递减顺序排列的整数数组
numbers
,请你从数组中找出两个数满足相加之和等于目标数target
。
函数应该以长度为2
的整数数组的形式返回这两个数的下标值。numbers
的下标 从1
开始计数 ,所以答案数组应当满足1 <= answer[0] < answer[1] <= numbers.length
。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例一:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
示例二:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
示例三:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解题方法:双指针
class Solution {
func twoSum(_ numbers: [Int], _ target: Int) -> [Int] {
let count = numbers.count
guard count > 2 else {
return [1, 2]
}
var left = 0
var right = count - 1
while left < right {
let value = numbers[left] + numbers[right]
if value > target {
right -= 1
} else if value < target {
left += 1
} else {
return [left + 1, right + 1]
}
}
return [-1, -1]
}
}
解题思路:
初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和目标值比较。如果两个元素之和等于目标值,则发现了唯一解。如果两个元素之和小于目标值,则将左侧指针右移一位。如果两个元素之和大于目标值,则将右侧指针左移一位。移动指针之后,重复上述操作,直到找到答案。
使用双指针的实质是缩小查找范围。那么会不会把可能的解过滤掉?答案是不会。假设 numbers[i]+numbers[j]=target
是唯一解,其中 0≤i<j≤numbers.length−1
。初始时两个指针分别指向下标 0
和下标 numbers.length−1
,左指针指向的下标小于或等于 i
,右指针指向的下标大于或等于 j
。除非初始时左指针和右指针已经位于下标 i
和 j
,否则一定是左指针先到达下标 i
的位置或者右指针先到达下标 j
的位置。
如果左指针先到达下标 i
的位置,此时右指针还在下标 j
的右侧,sum>target
,因此一定是右指针左移,左指针不可能移到 i
的右侧。
如果右指针先到达下标 j
的位置,此时左指针还在下标 ii 的左侧,sum<target
,因此一定是左指针右移,右指针不可能移到 j
的左侧。
由此可见,在整个移动过程中,左指针不可能移到 i
的右侧,右指针不可能移到 j
的左侧,因此不会把可能的解过滤掉。由于题目确保有唯一的答案,因此使用双指针一定可以找到答案。
复杂度分析
- 时间复杂度:
O(n)
,其中n
为数组的长度。两个指针移动的总次数最多为n
次。 - 空间复杂度:
O(1)
。