Leetcode 167:Two Sum II - Input array is sorted
阿新 • • 發佈:2021-02-02
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.
說人話:
一個有序陣列中找 2 個元素和為 target,返回它們的索引(索引從1開始)。
幾個要點:
- 陣列本身有序
- 索引從1開始計算
- 一個元素不能使用2次
- 本題保證有解、且是唯一解
[法1] 暴力法
思路
暴力解法思路非常清晰,就是雙層遍歷陣列。
程式碼
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] result = new int[2];
for(int i=0;i<numbers.length-1;i++){
for(int j=i+1; j<numbers.length;j++){
if((numbers[i] + numbers[j]) == target){
result[0] = i+1;
result[ 1] = j+1;
return result;
}
}
}
return result;
}
}
提交結果
程式碼分析
- 時間複雜度:雙層遍歷,很明顯是 O(n2)
- 空間複雜度:O(1)
改進思路
很明顯這裡時間複雜度 O(n2) 是比較大的,我們改進的方向應該就是想辦法儘可能少遍歷陣列。
暴力演算法中雙層遍歷並沒有利用到題目給的一個有用資訊:陣列本身有序
,所以我們可以從這個突破口尋求改進的方法。
[法2] 二分查詢
思路
因為陣列本身有序,那麼我們就想到了二分查詢法
。所以我們在確定了 nums[i] 後要找 nums[j] 的時候,這個時候找 nums[j] 就可以用二分查詢,這樣時間複雜度就優化為 O(nlogn) 了。
程式碼
class Solution {
public int[] twoSum(int[] numbers, int target) {
//結果
int[] result = new int[2];
//遍歷,首先確定 numbers[i]
for(int i=0;i<numbers.length-1;i++){
//二分查詢 numbers[j]
int jIndex = binarySearch(numbers,i+1,numbers.length-1, target-numbers[i]);
if( jIndex != -1){
result[0] = i+1;
result[1] = jIndex+1;
}
}
return result;
}
//二分查詢法
public int binarySearch(int[] nums, int start, int end, int target){
//從 [start...end] 中查詢
int l = start;
int r = end;
while( l<=r ){
int mid = l + (r-l)/2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < target){
l = mid+1;
}else if(nums[mid] > target){
r = mid-1;
}
}
return -1;
}
}
提交結果
程式碼分析
- 時間複雜度:因為第一層遍歷是迴圈遍歷,第二層遍歷是二分查詢,故時間複雜度是 O(nlogn)
- 空間複雜度:O(1)
改進思路
這次我們用上了陣列的有序性這個有利條件,大大降低了時間複雜度。但是有沒有可能只遍歷一次陣列就解決問題呢?
[法3] 對撞指標
思路
因為陣列本身是有序的,所以我們可以利用這個有序性,建立兩個指標,一個在頭一個在尾,兩個不斷向中間夾,直到找到兩個和為 target 的元素。
整體思路:
- 建立頭指標 head 和 尾指標 tail
- 計算 numbers[head] + numbers[tail] 的值
- 如果 == target,完成
- 如果 < target,則說明 numbers[head] 不夠大,就 head++
- 如果 > target,則說明 numbers[tail] 太大,就 tail–
- 不斷向中間夾,直到 numbers[head] + numbers[tail] == target
- 如果 head = tail,這說明找不到
程式碼
class Solution {
public int[] twoSum(int[] numbers, int target) {
//頭指標
int head = 0;
//尾指標
int tail = numbers.length-1;
//結果
int[] result = new int[2];
//兩邊夾
while(head < tail){
int sum = numbers[head] + numbers[tail];
//== target,完成
if(sum == target){
result[0] = head+1;
result[1] = tail+1;
break;
}
// < target,說明 numbers[head] 太小,head++
else if(sum < target){
head++;
}
// > target,說明 numbers[tail] 太大,tail++
else if(sum > target){
tail--;
}
}
return result;
}
}
提交結果
程式碼分析
- 時間複雜度:O(n)
- 空間複雜度:O(1)