题目:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
我的答案——暴力双重循环
答案代码
class Solution {
public int[] twoSum(int[] nums, int target) {
int [] ret = new int[2];
boolean flag = false;
for(int i = 0; i < nums.length-1; i++)
{
if(flag)
{
break;
}
for(int j=i+1; j < nums.length; j++)
{
if(nums[i] + nums[j] == target)
{
ret[0] = i;
ret[1] = j;
flag = true;
break;
}
}
}
return ret;
}
}
答案思路
要从一个数组中找到两个数之和为给定数,我想到的就是暴力查询,由于同样的数不能用两次,所以内层循环是从i+1开始,又因为j是要小于nums.length的,所以外层循环是到nums.length-1结束。咋内层循环体中,判断nums[i] + nums[j]是否等于target,如果等于就Break,不等就继续向下。双层的循环没办法用一个break进行解决,所以我添加了一个判断条件,来控制外层循环。
解答——两遍哈希
代码
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 need = target-nums[i];
if(map.containsKey(need) && map.get(need) != i)
{
return new int[]{i, map.get(need)};
}
}
throw new IllegalArgumentException("no such two numbers");
}
}
思路
主要分为几个点:
1. 为什么要将值作为key而下标作为value
2. 如果数组中的值出现了重复,hashMap会丢掉前一个下标,答案不会出错吗?
首先,为什么要用哈希表?因为哈希表的查找速度很快,可以将O(n)降到O(1),这里是采用了使用空间来换取时间的方法,不需要嵌套循环就可以查找出满足题目要求的数组下标。即便数组中有重复的值也不会出错。
哈希表可以通过函数 containsKey( ) 来判断表中是否存在某个key,而且可以通过key使用get() 来取出value值。
循环遍历数组nums,将数组中的值存入哈希表中,值为Key而下标为value。
再次循环数组nums,从0开始循环,取出nums[i],用target减去nums[i],我们就得到了如果当前 i 是答案的其中一个下标的话,那么另一个答案的值应为多少,将这个值存放在need变量中,然后到hashmap中使用containsKey去查询是否存在key为need的键值对,如果存在,取出need对应的value值也就是下标,查看是否与当前的 i 相同(题目要求不能为同一个元素),若不同,则查询结束,返回当前 i 的值以及need对应的value值。
其实这边有一个很巧妙的点(或许不算巧妙八,只是我太菜),我在做的时候,只考虑到了将两个数相加来判断是否与target相等,没有考虑到可以用target来减去一个数得出结果然后判断数组中是否存在这个得出的结果。这样思考的话,就把思路从根据下标找数变成了根据数来找下标。
然后说明为什么即便数组中存在两个相同的数也能得出正确的结果。我们知道,哈希表中的Key是不能相同的,而如果数组中存在两个相同的数的话,在第一次循环的时候,第二个数的下标会覆盖掉第一个数的下标,注意,是下标,这很重要。虽然覆盖了,但是我们第二次循环循环的是数组,是从数组的下标0开始的,我举个栗子,测试用例数组为[2,2,3,4],target为4,我们直接看的话可以知道这个用例的返回值应该为[0,1]。而程序运行也会给我们相同的结果。第一次循环,将数组的值和下标存放到hashmap中:<2,1> <3,2><4,3> ; 第二次循环,i从0开始,取出nums[i]为2,用target减去2等于2,也就是说,如果hashmap中存在一个Key为2的键值对,且value不等于i,就说明找到了结果。主要是,循环数组的时候是从前向后循环,而hashmap中存放的是后一个数的下标,如果题目target只能由重复的数得到,那么在循环到第一个数的时候,就可以直接返回正确的结果了。这里可以看出,正是由于hashmap中无法存放两个相同的kay,题目才能得到正确的输出。
解答——一遍哈希
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++)
{
int need = target - nums[i];
if(map.containsKey(need))
{
return new int[]{ map.get(need),i};
}
map.put(nums[i] ,i);
}
throw new IllegalArgumentException();
}
}
思路
在两遍哈希的时候,我们是第一次循环将数组中的值保存进去,第二次循环的时候再去查询匹配,那么我们可不可以在一次循环中就搞定呢?当然可以。在循环的时候,我们先判断hashmap中是否存在能与nums[i]相加为target的键存在,如果不存在,将当前的nums[i]和下标i存放到hashmap中,i++,继续判断。
也就是说,数组中的每一个都只需要和在它前面的数进行匹配,因为在它后面的数会去匹配它,如果匹配不上的话,把自己也加入到Hashmap中,交给后面的数去匹配。