两数之和 II - 输入有序数组
原题链接:https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/
题目
题解
解法一:利用HashMap
题目确保仅存在一个有效答案,非递减序列,不可使用同一个元素。
思路:采用for循环遍历,利用HashMap可以将当前元素的信息进行保存,方便后续查询。key:该数 的值,value:该数对应的下标
- 计算 target 减去 当前元素的值,得到需要查询的值 x
- 利用HashMap的containKey()方法,查询之前该数是否出现过,如果已出现则该数取对应的value值,return {该数下标,当前元素下标}
- 若未出现过,将当前数和对应下标存入HashMap中
class Solution {
public int[] twoSum(int[] numbers, int target) {
Map<Integer,Integer> map = new HashMap<>();
int n = numbers.length;
map.put(numbers[0], 1);
for (int i = 1; i < n; i++){
int x = target - numbers[i];
if (map.containsKey(x)){
return new int[] {map.get(x), i+1};
}
map.put(numbers[i], i+1);
}
return new int[]{};
}
}
复杂度分析:
- 时间复杂度:O(n)
解法一利用Map集合的特点,思路简单,但多次调用方法,耗时长
解法二:巧用数组
题目的数据范围和大小都不大,-1000<=numbers[i]<=1000,那两数之和的范围就在-2000<=sum<=2000。可以利用数组来代替HashMap,数组的优点是:查询效率快,只要保存出现的数和对应的下标就行了,我们可以把数组下标看作key,该下标存储的内容就是该数在原数组的下标,等价于HashMap的value。题目下标要求从1开始,那我们进行查询时,如果不为0,说明之前出现过,为0则未出现过,把该数记录存储下来。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int a[] = new int[2010];
int n = numbers.length;
a[numbers[0]+1000] = 1;
for(int i = 1; i < n; i++){
int x = target - numbers[i] + 1000;
if (a[x] != 0){
return new int[] {a[x], i+1};
}
a[numbers[i]+1000] = i + 1;
}
return new int[]{};
}
}
复杂度分析:
- 时间复杂度:O(n)
- 空间复杂度: O(2*n)
解法二利用数组查询效率快,但当数据范围变大时,如 -10^8 <= numbers[i] <= 10^8,那可能无法开辟出如此多的连续内存空间,及时开辟出那也会占用很大内存,且空间利用率也不高。
解法三:双指针
初始时左指针指向第一个元素,右指针指向最后一个元素。每次都计算左右两指针之和,如果该和等于目标值,则直接返回左右指针对应的下标{l+1,r+1};如果该和小于目标值,将左指针右移;如果该和大于目标值,右指针左移;如此往复判断,就可以找到对应下标。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum < target) {
i++;
} else if (sum > target) {
j--;
} else {
return new int[]{i+1, j+1};
}
}
return new int[]{};
}
}
复杂度分析:
- 时间复杂度:O(n),其中 n 是数组的长度。两个指针移动的总次数最多为 n 次
- 空间复杂度: O(1)
解法三:与解法二进行比较,该题的数据范围无法体现出双指针的优势,但该种方法无需再内存中开辟连续空间,当数据范围变大时,解法二会超出内存限制,无法运行。综合来看,解法三才是该题的优质解法。