題目說明
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那兩個整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重復利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解題思路1:窮舉法
從題目意思理解,就是從給定的整數數組中找到兩個整數,使得它們的和與給定的數相等。那最簡單粗暴的方式就是枚舉了,嗯,先來試試最簡單的。
class Solution {
public int[] twoSum(int[] nums, int target) {
return exhaustAlgorithm(nums,target);
}
// 窮舉法
private int[] exhaustAlgorithm(int[] nums, int target){
int length = nums.length;
int i = 0;
int j = 1;
while (nums[i] + nums[j] != target) {
j++;
if (j >= length){
i++;
if (i >= length - 1){
break;
}
j = i + 1;
}
}
// 說明不存在這樣的組合
if (nums[i] + nums[j] != target) return null;
int[] result = {i,j};
return result;
}
}
時間復雜度:\(O(n^2)\)
運行結果如下:
80ms,才擊敗了11.13%的用戶,說明優化空間還很大。
解題思路2:倒推法
窮舉法的效率一般都比較差,所以需要嘗試一些新姿勢。我們再來分析一下上面的窮舉算法,要從一個集合中找出兩個數,使得它們的和與給出的數target
相等,使用窮舉算法時,當我們選出第一個數a
后,需要循環遍歷之后的數,然后一一進行加和判斷,但實際上,我們只需要知道剩下的數里,有沒有數等於target - a
即可,而每次從數組中找到某個數是否存在,都需要遍歷一次,因此,更好的做法是將數與對應的序號存到一個map中,這樣就能將查找效率從\(O(n)\)提高到\(O(1)\)。
class Solution {
public int[] twoSum(int[] nums, int target) {
return mapSolution(nums,target);
}
// 倒推法
private int[] mapSolution(int[] nums, int target){
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++){
map.put(nums[i],i);
}
for (int i = 0; i < nums.length; i++){
int num = target - nums[i];
// 判斷num是否存在,如果已經存在,則直接返回
if (map.get(num) != null){
return new int[] { map.get(num), i};
}
}
return null;
}
}
這里我們對nums數組進行了兩次遍歷,第一次遍歷是將所有元素都存入map中,第二次遍歷是查找目標的整數對是否存在。
但再仔細想想,是否還能再優化呢?
答案是肯定的,在這個題中,要尋找的整數是成對存在的,所以我們可以只進行一次遍歷。
如果target
減去當前遍歷數值后的數不存在於map
中,則將當前數值與序號的映射關系存入map
中。也許你會問,那找到第一個要尋找的數時,第二個數顯然還不在map
中,那怎么辦呢?別着急,前面已經說過了,因為要尋找的數是成對存在的,這里我們假設為a
和b
,所以遇到第一個數a
時,由於b
還沒有存入map
,所以先將a
存入map
中,我們在找到第二個數b
后,此時a
已經在map
中了,所以就能在一次遍歷中順利找到了這對我們想要的整數了。
class Solution {
public int[] twoSum(int[] nums, int target) {
return mapSolution(nums,target);
}
// 倒推法
private int[] mapSolution(int[] nums, int target){
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++){
int num = target - nums[i];
// 判斷num是否存在,如果已經存在,則直接返回
if (map.get(num) != null){
return new int[] { map.get(num), i};
}
// 不存在則當前數值與序號的映射關系存入map中
map.put(nums[i], i);
}
return null;
}
}
時間復雜度:\(O(n)\)
空間復雜度:\(O(n)\)
運行結果如下:
一下降到了9ms,效率大大提升,擊敗85%的用戶,嗯,看來效果確實很顯著。
如果你有更好的想法,也歡迎留言交流討論~