Leetcode 01 - 两数之和 - 题解以及解析
题目描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
提交答案
class Solution {
public int[] twoSum(int[] nums, int target) {
final HashMap<Integer, Integer> numbers = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
final int remain = target - nums[i];
final boolean isContainRemain = numbers.containsKey(remain);
if (isContainRemain) {
return new int[]{numbers.get(remain), i};
} else {
numbers.put(nums[i], i);
}
}
return new int[]{-1, -1};
}
}
执行用时 : 2 ms , 在所有 Java 提交中击败了 99.58% 的用户
内存消耗 : 39.9 MB , 在所有 Java 提交中击败了 5.10%
题解反思
从提交的结果反馈来看,执行用时还是可以,但是内存消耗比较严重,是有优化的空间的。
整体解题思路为:采用一个 HashMap 依次存放原数组中的数,其中 key 为数值,value 为数值的索引。循环遍历原数组中的值,在每次循环中,先计算出 target 和当前循环中数值的差值,如果该差值在 HashMap 中存在,则返回 HashMap 中对应的索引和当前循环中的索引,否则将该循环中的数值和索引加入到 HashMap 中。
因此,该实现最终的时间复杂度为O(n),主要用在循环原数组,而从 HashMap 中查找结果的时间复杂度近似为O(1)。空间复杂度为O(n),主要用在 HashMap 中,最差情况下,该哈希表中将会存储原数组中的所有数值。
看了些许大家对这道题的讨论,采用 HashMap 解决这道题几乎是比较通用的解决方案,而空间复杂度O(n)暂时还没有找到更好的方法来优化它。
还要记录一下在做这道题的过程中,花费了较长时间思考的一个点。就是 HashMap 中谁为key 谁为 value 的问题。我再刚开始实现时,将数值的索引设置为 key, 将数值设置为 value,这就导致我在循环中从该 HashMap 中查找该数值及其索引时非常别扭,因为要先根据差值是否在 HashMap 的 values 中,当找到时,并不能直接用此 value 来得出其对应的索引,还要再循环一遍 HashMap 以找到该值所对应的索引,这样就多此一举,非常啰嗦了。而将 key 和 value 的位置调换之后这个问题就迎刃而解了。
今后,再面临需要采用 HashMap 数据类型存储数据时,可以考虑将需要返回的最终结果设置为value,而方便用来索引的值设置为key。在本例中,我们最终需要的结果是某数值所对应的索引,固为value,而我们需要依据数值来查找该索引,固为key。相信这个办法能够更好的帮助我们确定 HashMap 的格式。而什么时候可以采用 HashMap 数据结构呢?在我们需要一种高效的方法O(1)来检查数组中是否存在目标元素,如果存在,找出它的某个属性(如索引)时,就可以采用 HashMap 数据结构。 因为保持数组中的每个元素与其某个属性相互对应的最好方法就属 HashMap 了。
此外,还发现一处需要改进的,就是没有找到满足条件时的返回值,在自己的答案中返回的是 [-1,-1],这里的 [-1,-1] 如果不是刻意规定其实是没有任何意义的。看到大部分的题解都是推荐 throw new IllegalArgumentException("No two sum solution") 处理的。之后对该情况应该再慎重考虑下,如果是面试或者平常写业务代码时,一定要和面试官或者业务人员确定好各种边缘情况下的返回值。
本文首发于「愚一笔记」。