本菜鸡在查阅力扣的解答参考资料时感觉其往往缺乏展示思考的过程,没有从简单解法到复杂解法的演化。只是一股脑丢出最优解,让人略感痛苦,因此写下这个博客,如有错误,望不吝赐教
问题描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标
注意:同一个元素不能使用两次
问题分析
要找到两个数组元素之和为目标值的元素位置,至少要将数组遍历一遍。容易想到,可以依次以数组的每一个元素作为第一个加数——a[i],再在剩余的元素中依次查找a[j] = target - a[i],j ≠ \neq =i
class Solution {
public int[] twoSum(int[] nums, int target) {
int [] res = new int [2];
for(int i = 0; i < nums.length; i++){
int leftVal = target - nums[i];
for(int j = 0; j < nums.length; j++){
if(i != j && nums[j] == leftVal){
res[0] = j;
res[1] = i;
break;
}
}
}
return res;
}
}
结果
结果分析
可以看出,执行速度较慢。基于该方法进行优化。可以看到内层循环是数组开头寻找,这一步有冗余
因为,如果在k ∈ \in ∈{0,j - 1}的范围存在a[k] = target - a[j]的话,在i = k的时候就已经能得到结果了(k < j)
因此二层循环以 i + 1的值做为起始索引。需要修改的地方如下所示
for(int j = i + 1; j < nums.length; j++)
结果
时间更短
进一步优化
我们发现主要的耗时用在了查找第二个加数上面。而查找算法可以使用HashMap来优化,哈希表可以在O(1)时间内搜索目标值
遍历数组,先判断在哈希表中有没有key = target - a[i]的存在
如果有, 查找结束,将该key对应的value拿出加入到结果数组中,将 i 也加入到结果数组中
如果没有,将(key , value) = (a[i] , i)
class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums == null)
return null;
int [] res = new int[2];
for(int i= 0; i< nums.length;i++){
int leftValue = target - nums[i];
for(int j = i+ 1; j < nums.length; j++){
if(nums[j] == leftValue) {
res[0] = i;
res[1] = j;
return res;
}
}
}
return res;
}
}
###结果
可以看出,时间损耗大大降低由原来的O(n^2)降低为O(2)
总结
做题目时,可以先从最简单的暴力求解做起。时间消耗过高时,先尝试对本方法的优化。再考虑用新的方法解决
本题的答案是通过两次优化得到的,主要的优化点是哈希表对搜索的时间的优化,但是空间复杂度上升为O(n)