吃一堑也不一定长一智,但是对吃一堑的反思可以长一智!
第一题
1. 题目要求
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, and you may not use the same element twice.
(给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。)
2. 示例
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
3. 我的思路
首先,要在一个数组中找到两个数和为指定值,那么我可以遍历一遍该数组,每次访问数组中的一个数,然后这个问题就转换为在剩余的元素里查找特定值(target-currentElement,其实是一个二重遍历了);自然是一个嵌套循环;代码实现也比较简单(就不加注释了,不能为了注释而注释,我的C#老师说,好的编程分格可以替代不少注释~ ,感觉通过变量名是可以明白思路的,毕竟是我是小白一个嘛~):
public int[] twoSum(int[] nums, int target) {
int firstNum;
int secondNum;
int targetNum;
int[] results=new int[2];
int numbersLength=nums.length;
for(int i=0;i<numbersLength;i++){
firstNum=nums[i];
targetNum=target-firstNum;
for(int j=i+1;j<numbersLength;j++){
secondNum=nums[j];
if(secondNum==targetNum){
results[0]=i;
results[1]=j;
break;
}
}
}
return results;
}
好的,没问题,可以通过(不通过我也不记在这里呀,但只是通过而已——29ms,超过40%的Java提交);这里的思悟是来源于我没有通过的版本:
- 第一次没有通过是因为内层循环变量j我写成i了(好吧,我承认我不是很习惯离开IDE写代码,但我正在努力尝试);
- 第二次没有通过是因为results[0]=firstNum;人家要的是索引,我返回的是值!(我承认这是因为我没有仔细看示例)
这两次都是因为“粗心”的原因,其实我知道“粗心”反映出的是我的编程习惯、处理问题的态度和方式上的缺陷。
4. 大神的解法
//运行时间7ms
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer>map=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
int complement=target-nums[i];
if(map.containsKey(complement)){
return new int[] {map.get(complement),i};
}
map.put(nums[i],i);
}
throw new IllegalArgumentException("no such two numbers");
}
//运行时间6ms
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> thisMap = new HashMap();
for(int i=0;i<nums.length;i++){
int tempTarget = target - nums[i];
if(thisMap.size()!=0 && thisMap.get(tempTarget) != null){
return new int[]{i,thisMap.get(tempTarget)};
}
thisMap.put(nums[i],i);
}
return null;
}
//运行时间5ms
public int[] twoSum(int[] numbers, int target) {
int [] res = new int[2];
if(numbers==null||numbers.length<2)
return res;
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < numbers.length; i++){
if(!map.containsKey(target-numbers[i])){
map.put(numbers[i],i);
}else{
res[0]= map.get(target-numbers[i]);
res[1]= i;
break;
}
}
return res;
}
其实5-7ms的差距并不是很大,而且可以看出他们的共同点是:
- 使用了HashMap这一数据结构;
- 因为使用了HashMap这一数据结构,不管数据是什么样的,他们都只需一遍遍历即可!
5. 我和大神的差距在哪里
首先,从看待问题的角度来说,我是将问题转换为对一个数的查找;而大神们的解法其核心是记录(加入HashMap)+判断(是否已找到解);而判断是利用HashMap的高效查找特性完成的!比我的循环+比较高了不知多少;
其次,算法之所以差(和好的比~ )一定是做了一些重复而无用的事情,浪费了时间(这就是少死脑细胞的代价吧~)。我多做了什么呢?我把嵌套遍历中的内循环给浪费了!我的目光在于一个特定的数,但是实际上我已经遍历了整个数组,也就是如果说我的内循环没有找到答案,那么这次循环对于我解决这个问题的作用就是0,而甚至我的外层循环在找到答案之前的遍历也是毫无价值!差的22ms就是这么被我浪费的;而大神们的解法中,当找不到解时,也可以为下一次找到解做出贡献!——每一次访问数组都是有价值的!