首先,最简单的方法是暴力法,就是遍历每个元素,然后对于每个元素,再遍历剩下的元素,看看有没有满足条件的。这样的话,时间复杂度是O(n²),对于数组很大的情况,比如n是10^4的话,这样的复杂度可能会超时。不过题目里给的进阶要求是希望时间复杂度小于O(n²),所以应该需要更高效的方法。
那有没有更好的方法呢?记得之前学过哈希表的方法,可以用空间换时间。具体来说,我们可以用一个哈希表来存储已经遍历过的元素的值和它们的下标。然后,对于当前元素nums[i],我们检查哈希表中是否存在target - nums[i]这个值。如果存在,那么说明之前已经有一个元素和当前元素的和等于target,这时候直接返回它们的下标就可以了。这样的话,只需要一次遍历,时间复杂度是O(n),空间复杂度也是O(n),因为哈希表存储了最多n个元素。
那这样的话,具体的步骤应该是这样的:
-
初始化一个空的哈希表,用来存储元素值和对应的下标。
-
遍历数组中的每一个元素nums[i]:
a. 计算差值complement = target - nums[i]。
b. 检查这个complement是否存在于哈希表中:
-
如果存在,则返回当前索引i和哈希表中存储的complement对应的索引。
-
如果不存在,则将当前元素nums[i]作为键,下标i作为值存入哈希表中。
- 由于题目保证有且只有一个答案,所以循环中一定能找到解,不需要处理无解的情况。
举个例子,比如示例1中的nums = [2,7,11,15], target=9。遍历到第一个元素2的时候,计算complement是7,此时哈希表为空,没有找到,所以将2存入哈希表。接着遍历到7,计算complement是9-7=2,这时候哈希表中存在2对应的下标0,所以返回[0,1]。这样就正确了。
如果数组中出现多个结果集,则会先返回第一个。例如:nums=[3,3,3],target=6。当数组遍历到第二个3时(即[3,1]),得到正确结果,代码结束,返回结果值。
代码
import java.util.HashMap;
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
// 查询哈希表中是否已存在键为complement的数据
if (map.containsKey(complement)) {
// map.get(complement)从哈希表中获取补数对应的索引
return new int[] { map.get(complement), i };
}
// 如果补数不存在,将当前元素的值nums[i]作为键,索引i作为值存入哈希表
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap();
for(int i = 0,j=nums.length-1; i<=j;){
int complement = target-nums[i];
if(map.containsKey(complement)){
return new int[]{i,map.get(complement)};
}
// i++ 先返回i的值,再加加
map.put(nums[i],i++);
if(map.containsKey(target-nums[j])){
return new int[]{j,map.get(target-nums[j])};
}
map.put(nums[j],j--);
}
return new int[]{};
}
}