【LeetCode每日一题】1. 两数之和
一、题目
原题地址:1.两数之和
二、题解
方法一:暴力枚举
1.思路及算法
-
最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。直接使用双重for循环两次遍历数组即可。
-
当我们使用遍历整个数组的方式寻找
target - x
时,需要注意到每一个位于x
之前的元素都已经和x
匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在x
后面的元素中寻找target - x
。
2.代码
/**
* @author : lightupworld
* @description: https://leetcode-cn.com/problems/two-sum/
* @date 2021/5/10 20:13
*/
public class TwoSum{
public static int[] twoSum(int[] nums, int target) {
//1.异常判断 (本题可以不用做异常判断,但是工作中异常判断是基础且必要的事情)
if (nums == null || nums.length == 0) {
return new int[0];
}
//2.双重for计算
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};
}
}
}
return new int[0];
}
//主函数 用于本地测试
public static void main(String[] args) {
int[] nums = new int[]{3, 3};
int target = 6;
int[] res = twoSum(nums, target);
for (int re : res) {
System.out.println(re);
}
}
}
3.复杂度分析
-
时间复杂度:O(N^2),其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。
-
空间复杂度:O(1)
4.注意事项
- ① 双重for循环注意i,j的起始位置,外层循环的应该是从数组下标
[0, length-1)
, 内层for循环的应该是[1,length)
- ② 数组初始化的三种方式
1.静态初始化:初始化时由程序员显式指定每个数组元素的初始值,有系统决定数组的长度; 示例: int[] intArr; intArr = new int[]{1,2,3,4,5,9}; 2.简化的静态初始化方式 示例: String[] strArr = {"张三","李四","王二麻"}; 2.动态初始化:初始化时由程序员指定数组的长度,由系统初始化每个数组元素的默认值。 示例: int[] price = new int[4];
- ③ 数组遍历的常用三种方式
1.for循环遍历 for(int i = 0; i < arr.length; i++){ System.out.print(arr[i]); } 2.foreach语句遍历 for {int a : arr}{ System.out.print(a); } 3.Arrays.toString转化成字符串输出 System.out.println(Arrays.toString(nums));
方法二:HashMap
1.思路及算法
-
注意到方法一的时间复杂度较高的原因是寻找
target - x
的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。 -
使用哈希表,可以将寻找
target - x
的时间复杂度降低到从 O(N)降低到 O(1)。 -
这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在
target - x
,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
2.代码
public class TwoSum {
public static int[] twoSum(int[] nums, int target) {
//1.异常判断
if (nums == null || nums.length == 0) {
return new int[0];
}
//Map map = new HashMap<Integer, Integer>(); 错误写法
Map<Integer,Integer> map = new HashMap();
for (int i = 0; i < nums.length; i++){
int numsJ = target - nums[i];
if (map.containsKey(numsJ)){
return new int[]{i,map.get(numsJ)};
}
map.put(nums[i],i);
}
return new int[0];
}
//主函数 用于本地测试
public static void main(String[] args) {
int[] nums = new int[]{3, 3};
int target = 6;
//System.out.println(Arrays.toString(nums));
int[] res = twoSum(nums, target);
System.out.println(Arrays.toString(res));
}
}
3.复杂度分析
-
时间复杂度:O(N),其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O(1)地寻找
target - x
。 -
空间复杂度:O(N)O(N),其中 NN 是数组中的元素数量。主要为哈希表的开销。
4.注意事项
- ①泛型的正确使用
1.标准写法 泛型的定义一定要放在前面 Map<Integer,Integer> map = new HashMap<Integer,Integer>(); 2.精简写法 Map<Integer,Integer> map = new HashMap<>(); Map<Integer,Integer> map = new HashMap(); 3.错误写法 Map map = new HashMap<Integer,Integer>(); 这种写法把泛型的定义放在了 new 后面, 这种写法泛型不会生效。如下图,不能正确识别所需要的类型,需要强制转成需要的类型。
- ② Java HashMap的常用方法
-
put()
: 将指定的键/值对插入到 HashMap 中。hashmap.put(K key,V value)
-
get()
:获取指定 key 对应对 valuehashmap.get(Object key)
-
getOrDefault()
:获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。hashmap.get(Object key, V defaultValue)
-
containsKey()
: 检查 hashMap 中是否存在指定的 key 对应的映射关系。存在指定的 key 对应的映射关系返回 true,否则返回 falsehashmap.containsKey(Object key)
-
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-solution/