题目:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
实例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
/**
* 暴力穷举法,有2点缺陷
* 1、使用了flag来标识是否结束,太麻烦了,不如直接return,以及使用了 i1 i2两个变量,没必要,直接new一个int[]更简洁
* 2、当没有结果的时候return null 不是很合理,毕竟题目说的是一定会有结果,因此改为抛出异常更合理一些
* @param nums
* @param target
* @return
*/
public static int[] numAdd(int[] nums,int target){
boolean flag = false;
int i1 = 0;
int i2 = 0;
for(int i = 0;i < nums.length - 1;i++){
for(int j = i+1;j < nums.length;j++){
if(nums[i] + nums[j] == target){
System.out.println("找到结果了");
i1 = i;
i2 = j;
flag = true;
break;
}
}
if(flag){
break;
}
}
if(flag){
int[] numss = {i1,i2};
return numss;
}else{
return null;
}
}
/**
* 暴力穷举法的改良版,更加简洁高效了
* @param nums
* @param target
* @return
*/
public static int[] numAdd2(int[] nums,int target){
for(int i = 0;i < nums.length - 1;i++){
for(int j = i+1;j < nums.length;j++){
if(nums[i] + nums[j] == target){
System.out.println("找到结果了");
return new int[]{i,j};
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
/**
* 以上方法最主要的缺陷还是双重for循环的效率很低,循环次数太多了,因此想办法优化
* 要看到这个问题的本质,其实就是一点:找到一个数组中我们所需要的特定值的下标
* 也就是说,我们需要找到一个特定值,同时找到它的索引,值与索引的关系让我们想到了 Hash算法
* 所以我们用HashMap来存数据,key为数组的值,value为数组索引,然后利用这个Map来进行操作
* 注意这里的key跟value不能搞反了,我们已知的是我们需要的目标值,需要找到的是目标值(如果有)的下标
* 因此key value就很明显了,弄反了的话还是一样需要循环这个map中的value Set集合,计算的复杂度大大提高,
* 可能会想到Map方法的一个API,是containsValue,利用这个表面上看不需要多一次循环,但是,想一想哈希算法的实现原理,就会知道
* 其实底层还是在循环的!所以不行
*
* 虽然还是有2个for循环,但是呢,不是嵌套的了,效率自然也会大大提高
*
* 可能会疑惑,要是数组中有元素值一样的元素的话,那么Map里面不就覆盖了吗?但是根据题意,我们只需要得出一个结果,重复的话并不影响我们结果的准确性!
*
* @param nums
* @param target
* @return
*/
public static int[] numAdd3(int[] nums,int target){
Map<Integer, Integer> map = new HashMap<Integer,Integer>();
for(int i = 0;i < nums.length;i++){
map.put(nums[i], i);
}
for(int j = 0;j < nums.length - 1;j++){
// 唯一需要注意的点是:不能用数组中同一个元素相加等于目标值
/*
Integer value = map.get(target - nums[j]);
if(value != null && value != j){
return new int[]{j,map.get(target - nums[j])};
}
*/
// 上面的写法也可以,但是不够简洁,利用已有API简化代码,如下
int targetValue = target - nums[j];
if(map.containsKey(targetValue) && map.get(targetValue) != j){
return new int[]{j,map.get(targetValue)};
}
}
throw new IllegalArgumentException("No two sum solution");
}
/**
* 该方法是上面利用哈希表(HashMap)的进化版,我们上面是先 循环 数组中的数据存到HashMap中,再用数组、HashMap进行 循环 计算
* 一共要2次循环,我们考虑能否只做一次循环,也就是在数据存到HashMap中的时候就直接进行比较,
* 分析:在把数组的元素放到Map中的时候,就看看Map中的元素与即将要丢进去的元素是不是满足要求 ,因为我们会遍历完数组的,所以这样也不会遗漏数据
* 并且,还有一个好处,就是不会有重复的元素出现了,因为都是即将放入的元素与Map中已有的元素进行比较的
*
* ps:唯一需要注意的是,Map中的value以及数组的下标在return的数组中哪个在前哪个在后的问题,我们按照习惯把小的放前面
*
* @param nums
* @param target
* @return
*/
public static int[] numAdd4(int[] nums,int target){
Map<Integer, Integer> map = new HashMap<Integer,Integer>();
for(int i = 0;i < nums.length - 1;i++){
int targetValue = target - nums[i];
if(map.containsKey(targetValue)){
return new int[]{map.get(targetValue),i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}