1.题目
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
解法一:双重for循环
伪代码展示:
class Solution {
public int[] twoSum(int[] nums, int target) {
//我们要找到2个数之和等于target
//即我们需要找到nums[i] + nums[j] == target,并且返回他们的下标,即i和j,其中i != j
int[] ans = new int[2]; //声明一个大小为2的数组用来保存结果
//我们通过循环来遍历所有的数字
int n = nums.length; //用一个变量len保存nums的长度
//i为第一个数的下标,nums一共有n个数,所以i的取值范围是[0, n-1]
for(int i = 0; i < n; i++){
//j为第二个数的下标。
for(int j = i + 1; j < n; j++){
//对于每个数nums[i],我们验证一遍其他数(nums[j])跟它的和是否等于target
//如果满足条件
if(nums[i] + nums[j] == target){
//将下标写入返回值数组里
ans[0] = i;
ans[1] = j;
//返回
return ans;
}
}
}
//默认返回值
return new int[0];
}
}
第一种方法比较容易想到,但是双重循环,但是时间复杂度有O(n^2),然后在此基础上,我们看下如何提升效能,减少时间复杂度。
我们来分析这个问题,首先第一层循环,我们遍历每个数nums[i],确定是否有跟它之和等于target的数,这步每个数肯定是必须执行一次的,无法优化。
第二层循环,我们是在找与nums[i]之和等于target的nums[j]。在二重循环里,我们使用了遍历(也就是顺序查找)的方式来确定这个nums[j],是否存在效率更高的查询方式呢?答案是存在的,我们可以通过哈希表的方式来在O(1)的时间复杂度里找到nums[j],从而使得总的时间复杂度变成O(n)。
解法二:利用HashMap提升效率
HashMap是一个由键值对key-value组成的数据结构,key是由哈希函数来获取的,读写效率都是O(1),是提升时间效率最常用的数据结构。
声明方式:HashMap<Integer, Integer> map = new HashMap<>();
首先是类型是HashMap,然后<Integer, Integer>表示key和value的类别都是Integer,Integer是int的包装类,可以近似认为跟int是一样的。变量名叫map。new表示创建对象,创建的是一个HashMap,后面的<>里面按完整写法也是需要写成<Integer, Integer>的,不过现在java有局部类型推断的,可以不写。
伪代码展示:
class Solution1 {
//已知一个数a,找到等于target - a的另一个数
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
//声明一个HashMap,key和value的类型都是int,变量名叫map,key和value分别记录数值和下标
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < n; i++){
//如果存在nums[j] + nums[i] == target,那么nums[j] = target - nums[i]
int diff = target - nums[i];
//如果存在diff,则返回diff和nums[i]的下标
//HashMap的containsKey方法判断该HashMap中是否存在这个key
if(map.containsKey(diff)){
//返回的时候直接创建一个数组,创建数组时可以直接初始化,就不需要填大小了,比如说new int[]{1, 2, 3};
//此时创建一个大小为2的数组,第一个元素为map.get(diff)也就是map中对应diff的value,第二个元素就是i,也就是2个数的下标
return new int[]{map.get(diff), i};
}else //否则,将nums[i]存入map中
map.put(nums[i], i);
}
return new int[0];
}
}
许久没接触算法题,结果昨天写的时候就只想出了第一种解法,第二种解法还是看了其他朋友的题解才学会的,方知路漫漫其修远兮。继续加油!