题目描述:
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
- 返回的下标值(index1 和 index2)不是从零开始的。
- 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9 输出: [1,2] 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
这道题和题库中的第一道题特别相似,唯一的不同就是这道题输入的是一个排序数组。方法也特别类似。详细请看第一篇博客:
https://blog.csdn.net/weixin_37850160/article/details/86500847
本题主要有三种方法:
第一种方法:暴力for循环:就是两重for循环挨个找,找到就返回对应的下标+1的值(因为返回的下标值(index1 和 index2)不是从零开始的。)。时间复杂度O(n^2).不推荐,简单但是时间复杂度太高。
第二种方法:map:将数组中的元素当作键,下标当作值,存储到map中。然后循环遍历原数组,判断目标数-数组中的元素是否在map中存在,并且,i还要小于,存在时的这个键对应的值的下标。满足这两个条件,然后输出数组对应的下标i,和map中满足条件的值。
第三种方法:对撞指针法:们首先判断首尾两项的和是不是 target,如果比 target 小,那么我们左边 (i)+1 位置的数(比左边位置的数大)再和右相相加,继续判断。如果比 target 大,那么我们右边 (j)-1 位置的数(比右边位置的数小)再和左相相加,继续判断。我们通过这样不断放缩的过程,就可以在 O(n) 的时间复杂度内找到对应的坐标位置。
具体看代码:
第一种方法:
Scanner sc = new Scanner ( System.in );
int target = sc.nextInt ();
int []a = new int[]{2,5,6,7,11,15};
// 第一种方法,for暴力求解,时间复杂度高
// index1 和 index2,其中 index1 必须小于 index2
for (int i = 0;i<a.length;i++){
for (int j = i+1;j<a.length;j++){
if (a[i]+a[j]==target&&i<j){
System.out.println("["+(i+1)+","+(j+1)+"]");
}
}
}
执行结果:
运行时间:
第二种方法:
// 第二种方法,map存储,时间复杂度低O(n)
Map<Integer,Integer> map = new HashMap <> ( );
for (int i = 0;i<a.length;i++){
map.put ( a[i],i );
}
for (int i = 0;i<a.length;i++){
if (map.containsKey (target-a[i])&&i<map.get ( target-a[i])){
System.out.println("["+(i+1)+","+(map.get ( target-a[i])+1)+"]");
}
}
执行时间:
第三种方法:
// 第三种对撞指针法(首位各一个指针):
// 首先判断首尾两项的和是不是 target,如果比 target 小,那么我们左边 (i)+1 位置的数(比左边位置的数大)再和右相相加,继续判断。如果比 target 大,那么我们右边 (j)-1 位置的数(比右边位置的数小)再和左相相加,继续判断。我们通过这样不断放缩的过程,就可以在 O(n) 的时间复杂度内找到对应的坐标位置。
int j= a.length-1;
for (int i = 0;i<a.length;){
if (a[i]+a[j]<target&&i<j){
i++;
}else if (a[i]+a[j]>target&&i<j){
j--;
}else{
System.out.println("["+(i+1)+","+(j+1)+"]");
break;
}
}
}
执行时间:
总结:这道题思路方法很多,但推荐使用第三种或者第二种方法,时间复杂度小,代码简单容易理解。
2019-3-6