您的点赞和关注是我坚持写作的最大动力,本人正在寻找测试开发的工作机会,欢迎邮件联系(log.v@qq.com)
概要
描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
图形介绍
分析
我们拥有的资源是一个数组和一个数字,这个数字代表两个数字的和,这两个数字从给定的数组中寻找,比如题目给定的是 9 ,我们就可以从数组中找到 2 和 7 ,这就是题目需要的一组结果,我们要做的就是从数组中找出这种数字组合,并获得组合中两个数字的下标
暴力法
暴力法肯定是脑海里第一个迸出的灵感,先以第一个数字为基准,看看后面的数字能否和它凑成结果,如果可以,则返回,如果不行则继续寻找,以第一个数字为基准完成遍历后就使用下一个数字,直到找到题目所需的数字组合,很容易得到以下代码
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) { // 遍历第一个加数后面的每一个数字
if (nums[j] == target - nums[i]) { // 判断第一个加数与第二个加数的和是否为目标值
return new int[] { i, j }; // 找到则返回该数字组合中两个数字的下标
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
如何优化?
首先我们要思考如下问题:
为什么暴力法遍历效果差?
因为我们要寻找两个数字,所以按照暴力法,对于每一个数组中的元素,我们都试图去寻找与之相加满足条件的数字,所以会额外增加一次 O ( n ) O(n) O(n) 的时间复杂度,该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。由于没有额外的数据结构使用,该算法的空间复杂度为 O ( 1 ) O(1) O(1)
如何提升搜索效率?
可以看到,提升该算法效率的关键就是提升搜索的效率,这里我们用到了 HashMap
作为优化工具,它可以带来高效的搜索接口,该接口的 时间复杂度可以达到
O
(
1
)
O(1)
O(1), 接下来我们就可以使用如下方案获取最终结果:
原理图
这就是两遍哈希表法,具体代码如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(); // 创建 Hash 表数据结构
for (int i = 0; i < nums.length; i++) { // 第一遍,构建哈希表
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) { // 第二遍
int complement = target - nums[i]; // 判断第二个加数是否在 HashMap 中,如果在则查找成功
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
其中 map.containsKey(complement)
即为
O
(
1
)
O(1)
O(1) 的查找方法,也是效率提升的关键,复杂度分析:
-
时间复杂度: O ( n ) O(n) O(n),
我们把包含有 n 个元素的列表遍历两次。由于哈希表将查找时间缩短到 O ( 1 ) O(1) O(1) ,所以时间复杂度为 O ( n ) O(n) O(n)。 -
空间复杂度: O ( n ) O(n) O(n),
所需的额外空间取决于哈希表中存储的元素数量,该表中存储了 n 个元素。
第一性原理
该问题其实是一个查找
类型的问题,而两次哈希表法,实质上是一种通过缓存来优化时间效率的手段,通过在 HashMap
这个缓存中查找第二个元素,大大提升了算法效率。