1. 程式人生 > >Input array is sorted三種解法對比

Input array is sorted三種解法對比

本文對比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),我認為已經算是一種不錯的解法,到此先告一段落。