本文对比LeetCode 167. Two Sum II - Input array is sorted三种不同的解法并进行优化,抛砖引玉,望各位大佬指教。
题目:
Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.
Note:
- Your returned answers (both index1 and index2) are not zero-based.
- You may assume that each input would have exactly one solution and you may not use the same element twice.
Example:
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.
第一种解法:暴力解法,双重循环。
class Solution {
//双重循环
public int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
for(int i=0;i<numbers.length;i++){
for(int j=0;j<numbers.length;j++)
if(numbers[i]+numbers[j]==target && i!=j){
if(numbers[i]<numbers[j]){
res[0]=i+1;
res[1]=j+1;
}else{
res[0]=j+1;
res[1]=i+1;
}
}
}
return res;
}
}
双重循环这种暴力解法时间复杂度O(n^2),显然不是一个好的方法,在这道题里面提交的时候会显示超时,pass!
第二种解法:既然数组已经有序,那你是否想到了利用二分查找。在数组中二分查找目标元素最简单的代码如下,需要注意其中的边界问题。
public int binarySearch(int[] arr, int target) {
int n = arr.length;
int l = 0, r = n - 1; //在[l,r]范围内寻找target
while (l <= r) { //当l==r时,区间[l,r]依然是有效的
int mid = (l + r) / 2;
if (arr[mid] == target) {
return mid;
}
if (target > arr[mid]) {
l = mid + 1; //target在[mid+1,r]中
} else {
r = mid - 1;
}
}
return -1;
}
本题中先选择一个元素x,然后利用二分查找在剩下的元素中查找(target-x),找到的话返回索引。
class Solution {
//二分搜索
public int[] twoSum(int[] numbers, int target) {
for(int i=0;i<numbers.length;i++){
int index = binarySearch(numbers,i+1,target-numbers[i]);
if(index !=-1){
System.out.println(index);
int[] res = new int[]{i+1,index+1};
return res;
}
}
return new int[2];
}
public int binarySearch(int[] arr, int l, int target){
int n = arr.length;
int r=n-1;
while(l<=r){
int mid = (l+r)/2;
if(arr[mid]==target){
return mid;
}
if(target > arr[mid]){
l = mid + 1;
}else{
r = mid -1;
}
}
return -1;
}
}
这种解法的时间复杂度提高到O(nlogn),貌似还可以。
第三种解法:到此,应该问自己,是否还可以继续优化呢?答案是肯定的。当然还可以利用经典的指针对撞的方法解决。指针对撞,即分别定义一个头指针和尾指针,分别数组的头和尾向中间移动去对撞,问题在对撞的过程中迎刃而解。指针对撞问题的详细分析在后面再总结分享。
class Solution {
//指针碰撞
public int[] twoSum(int[] numbers, int target) {
int n=numbers.length;
int i=0,j=n-1;
while(numbers[i]+numbers[j] != target){
if(numbers[i]+numbers[j] > target){
j--;
}else{
i++;
}
}
int[] res = new int[]{i+1,j+1};
return res;
}
}
指针碰撞解法的时间复杂度为O(n),我认为已经算是一种不错的解法,到此先告一段落。