题目链接:https://leetcode.com/problems/two-sum/
题目:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
方法一暴力法:
暴力法很简单,遍历每个元素 xx,并查找是否存在一个值与 target - xtarget−x 相等的目标元素。
这道题一看就知道用暴力搜索肯定没问题,leetcode的时间为56ms,空间为38.1MB,这个算法的时间复杂度是O(n^2)。
class Solution {
public int[] twoSum(int[] nums, int target) {
int [] res = new int[2];
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){ //注意这里i+1
if(nums[i]+nums[j]==target){
res[0] = i;
res[1] = j;
}
}
}
return res;
}
}
复杂度分析:
时间复杂度:O(n^2)
对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费 O(n)O(n) 的时间。因此时间复杂度为 O(n^2)。
空间复杂度:O(1)。
后进行优化,考虑到空间限制,直接return new int []{,,,} return null 21ms,37.4MB
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i=0; i< nums.length;i++){
for(int j=i+1; j<nums.length;j++){
if(nums[i] + nums[j] == target){
return new int[]{i,j};
}
}
}
// throw new IllegalArgumentException("No two sum solution");
return null;
}
}
后看题解答案,得到如下solution,15ms,37.3MB 想了一下,所给答案之所以更快可能是因为JIT(just-in-time)技术发生了作用。因为在每一次最外层的遍历中,target - nums[i] 都是一个定值,在程序的内层遍历中,程序字节码经过JIT环境变量进行判断,属于“热点代码”(多次调用的方法,或循环等), 所以JIT技术会发生作用。
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i=0; i< nums.length;i++){
for(int j=i+1; j<nums.length;j++){
if(nums[j] == target-nums[i]){
return new int[]{i,j};
}
}
}
return null;
}
}
方法二两遍哈希表:
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
通过以空间换取速度的方式,我们可以将查找时间从 O(n)降低到 O(1)。哈希表正是为此目的而构建的,它支持以 近似 恒定的时间进行快速查找。我用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到 O(n)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为 O(1)。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i])是否存在于表中。注意,该目标元素不能是 nums[i] 本身!
class Solution {
public int[] twoSum(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 complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
复杂度分析:
时间复杂度:O(n),
我们把包含有 n 个元素的列表遍历两次。由于哈希表将查找时间缩短到 O(1) ,所以时间复杂度为 O(n)。
空间复杂度:O(n),
所需的额外空间取决于哈希表中存储的元素数量,该表中存储了 n 个元素。
方法三一遍哈希表:
事实证明,我们可以一次完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。
解析:利用hashmap,key存放数值,value存放出现的位置。从前到后进行遍历,将target值减去当前的值,看是否存在map中,
若存在map中则取出相应的标号,退出。 3ms,38.2MB
public class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int result[] = new int[2];
for (int i = 0; i < nums.length; i++){
int num = target - nums[i];
if(map.containsKey(num)){
result[0] = map.get(num);
result[1] = i;
return result;
}
map.put(nums[i], i);
}
return result;
}
}
复杂度分析:
时间复杂度:O(n) ,
我们只遍历了包含有 n 个元素的列表一次。在表中进行的每次查找只花费 O(1) 的时间。
空间复杂度:O(n),
所需的额外空间取决于哈希表中存储的元素数量,该表最多需要存储 n 个元素。