Leetcode 1:Two Sum
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
说人话:
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
几个要点:
- 本题有唯一解
- 元素不能重复使用
- 索引从 0 开始计算
举例:
[法1] 双层 for 暴力法
思路
暴力法的思路非常简单,就是遍历数组,两两相加,找到了就返回。
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
//数组长度必须大于等于2
if (nums.length < 2){
throw new RuntimeException();
}
//双层遍历,两两检验
for (int i = 0; i < nums.length-1; i++) {
for (int j = i+1; j < nums.length; j++) {
if (nums[i] + nums[j] == target){
return new int[]{i,j};
}
}
}
//没找到
throw new RuntimeException();
}
}
提交结果
代码分析
- 时间复杂度:O(n2)
- 空间复杂度:O(1)
改进思路
暴力法非常简单,但是由于使用了两个 for 循环,时间复杂度还是处于 O(n2) 这样一个比较大的水平。这里我们可以先对数组进行排序,然后使用双索引对撞来优化算法,使得时间复杂度更低。
但是由于题目要我们返回元素的索引,所以排序过后元素所对应的索引就发生了变化,这里我们需要想办法记录下元素原先的索引,比如可以封装成一个类,或者封装在 Map 中。方法非常多。不过该思路也比较负责,这里先不实现了。
下面使用查找表来解决这个问题。
[法2] 查找表 Map
思路
将所有元素放入查找表 Map 中,之后对于每一个元素 a
,查找 target - a
是否存在。
这样就存在一个问题了,如果我们把数组中的元素存在 Map 中,因为 Map 的 key 的唯一的,所以如果我们有重复元素的话,是会覆盖掉的,就会出问题。
这里的解决思路是,当我们在找 nums[i] 需要的另一个元素的时候,只找 nums[i-1] 中里面的,而 nums[0…i-1] 我们会放到 Map 中,这样就算 nums[0…i-1] 里面有值等于 nums[i] 的,由于我们的 nums[i] 还没让到 Map 里面,所以不会造成覆盖。
- 如果这个时候恰好找到了,那就直接返回结果就可以了
- 如果这个时候还没有找到,那就将 nums[i] 放到 Map 中。这个时候不用担心覆盖的问题了,因为即使有覆盖,我们在这个时候也已经证明了 nums[i] × 2 不会等于 target 了,覆盖了也没关系。
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
//需要的第二个元素
int secondEle = target - nums[i];
//在 nums[0....i-1] 中寻找
if (map.containsKey(secondEle)){
//找到了返回
return new int[]{i,map.get(secondEle)};
}else{
//找不到就把 nums[i] 放入 map 中
map.put(nums[i],i);
}
}
throw new RuntimeException();
}
}
提交结果
代码分析
- 时间复杂度:扫描了一遍 nums,而 map 中查找是 O(1),所以总共是 O(n)
- 空间复杂度:O(n)