目录
两数之和(LeetCode01:Array And HashMap)
1、解题三部曲
在具体做题的时候,可以采用以下三个步骤来进行。
拿到题目后,不要立马开干,想着下面的三个步骤,一步一步地来。
1.1、 看懂题目
看懂题目。有的题目很直接,直接告诉你要解决的问题是什么,题目本身甚至都包含了对应的数据结构和需要用到的算法;有的题目很隐晦,看了半天不知道它到底要解决什么问题,可以用什么算法和数据结构来解。所以,看到题目后,一定要先确保自己理解清楚了。
我的一个经验是,拿到一个题目后,看5分钟,如果5分钟之内看不懂,我就mark 下来,留到后面再做,要不很影响刷题的心情。
不过就leetcode 来说,这样的题目不多。基本都能在再5分钟内看懂。
1.2、分析,推导解法
分析推导题目的解法。
这个步骤要有意识地单独拎出来,不要跟编码步骤混淆在一起。也就是说,你在分析推导题目解法的时候,不要去想任何实现相关地事情,不用去想代码怎么写,不用去想要用什么库,定义什么变量,用多少层循环,都不要想,就想着在逻辑上,这道题目要怎么解。
这样做可以极大地降低你的心智负担,使你高效地想出题目的解法。对于如何将想法变成代码,可以留在下一个步骤,单独来进行。
1.3、将思路转换为代码
当你确定题目都已经理解,并且分析推导出了题目的解法后,你才开始来思考如何将自己的思路转换成代码。是地,将思路转换成代码,可以是一个单独地步骤,在实际工作中,其实也是很重要的一个能力。
有时,将一个思路转换成算法是很容易且自然的;但有时,有些思路转换成代码,是很有难度的事情。
或者你有体会,分析推导只用了不到十分钟,结果代码写了半小时还写不完整。
怎么定义变量,保存状态,用递归,还是用循环加辅助数据结构等等,都是将思路转换成代码要做的事情。
这个能力也需要刻意地去练习。
总结
首先算法不是只拼智商的,是可以通过后天的刻意练习掌握的一种能力。
刚上手的时候,难度上需要循序渐进,最好能够按算法分类来刷题。
解题的时候,建议按这三个步骤来
1,看懂题目
2,分析,推导解法
3,将思路转换为代码。
在更细节方面,封装的思想也可使用在算法上面,可以极大地降低我们的心智负担,提升解题的效率。
最后是要注意做题过程中的正反馈,确保自己能持续地做下去。
2、题目
题目来源:https://leetcode-cn.com/problems/two-sum/
两数之和 Two Sum
给定一个整数数组 nums 和一个目标值 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) 的算法吗?
3、解题过程
这道题给了我们一个数组,还有一个目标数target,让我们找到两个数字,使其和为target,乍一看就感觉可以用暴力搜索,但是猜到OJ肯定不会允许用暴力搜索这么简单的方法,于是去试了一下,果然是Time Limit Exceeded,这个算法的时间复杂度是O(n^2)。
class Solution {
public static int[] twoSum(int[] nums, int target) {
int[] res = {0, 0};
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
res[0] = i;
res[1] = j;
}
}
}
return res;
}
public static void main(String[] args) {
int[] res = twoSum(new int[]{2, 7, 11, 15}, 9);
System.out.println();
}
}
那么只能想个O(n)的算法来实现,由于暴力搜索的方法是遍历所有的两个数字的组合,然后算其和,这样虽然节省了空间,但是时间复杂度高。一般来说,我们为了提高时间的复杂度,需要用空间来换,这算是一个trade off吧,我们只想用线性的时间复杂度来解决问题,那么就是说只能遍历一个数字,那么另一个数字呢,我们可以事先将其存储起来,使用一个HashMap,来建立数字和其坐标位置之间的映射,我们都知道HashMap是常数级的查找效率,这样,我们在遍历数组的时候,用target减去遍历到的数字,就是另一个需要的数字了,直接在HashMap中查找其是否存在即可,注意要判断查找到的数字不是第一个数字,比如target是4,遍历到了一个2,那么另外一个2不能是之前那个2,整个实现步骤为:先遍历一遍数组,建立HashMap映射,然后再遍历一遍,开始查找,找到则记录index。代码如下:
Java 解法一:
class Solution2 {
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
res[0] = i;
res[1] = map.get(target - nums[i]);
break;
}
map.put(nums[i], i);
}
return res;
}
public static void main(String[] args) {
int[] res = twoSum(new int[]{2, 7, 11, 15}, 9);
System.out.println(res[0]);
System.out.println(res[1]);
}
}