HOT100 第一题,也是力扣第一题
链接:1. 两数之和 - 力扣(LeetCode)
时间:2026年4月10日20:22:47
思路:这个问题本质上是在找数对,第一想法是枚举每个数对(i,j),第一个迭代变量迭代i从0到n,第二个迭代变量迭代j从i+1到n,这样就可以不重复的枚举不同位置的数对,然后通过判断其和是否等于target。由于本题问题规模n上限为,这种方法的时间复杂度为O(),也可以过。
写法一:
class Solution {
public int[] twoSum(int[] nums, int target) {
// 暴力解法:枚举所有可能的两个数
// 看它们的和是否等于 target
int n = nums.length; // 数组长度
// 第一层循环:固定第一个数 nums[i]
for (int i = 0; i < n; ++i) {
// 第二层循环:从 i 的后一个位置开始找第二个数 nums[j]
// 这样可以避免重复枚举,例如 (0,1) 和 (1,0) 只算一次
for (int j = i + 1; j < n; ++j) {
// 如果当前这两个数的和正好等于目标值
if (nums[i] + nums[j] == target) {
// 返回这两个数的下标
return new int[] { i, j };
}
}
}
// 如果遍历完所有数对都没有找到
// 按题意一般不会发生,这里只是为了代码完整性
return new int[] { -1, -1 };
}
}
写法二:我可以用另一种枚举写法:先枚举第二个数,迭代变量j从0到n-1,然后再枚举第一个数,迭代变量i从0到i-1,这样也可以覆盖到每一种数对<i,j>
class Solution {
public int[] twoSum(int[] nums, int target) {
// 暴力解法:枚举所有可能的两个数(i,j)
// 看它们的和是否等于 target
int n = nums.length; // 数组长度
// 第一层循环:固定第二个数 nums[j]
for (int j = 0; j < n; ++j) {
// 第二层循环:从0到j-1 枚举第一个数nums[i]
// 这样可以避免重复枚举,例如 (0,1) 和 (1,0) 只算一次
for (int i = 0; i < j; ++i) {
// 如果当前这两个数的和正好等于目标值
if (nums[i] + nums[j] == target) {
// 返回这两个数的下标
return new int[] { j, i };
}
}
}
// 如果遍历完所有数对都没有找到
// 按题意一般不会发生,这里只是为了代码完整性
return new int[] { -1, -1 };
}
}
时间复杂度O(),空间复杂度(O(1))。
考虑进一步优化:用O($n^2$)去枚举数对的过程中发现【特别是从写法二可以看出】,对于某下标而言,我其实只需要看他之前的元素【或者他之后的元素即可】,比如说写法二中,我看当固定第二个迭代变量j后,其实我只需要看j之前包不包含`target-nums[j]`即可,而这种枚举在每次固定j后都会查询诸如j=2时需要查询nums[0]+nums[2],nums[1]+nums[2], j=3时候需要查nums[0] + nums[3],nums[1] + nums[3],nums[2]+nums[3],相当于每次都要去数组中查询nums[0],nums[1],nums[2],所以造成了这些元素一共要在数组中被访问n次、n-1次、n-2次……,但是如果我们此时使用哈希表记录访问过的数,那么就可以将这时的时间复杂度压缩到O(1),相当于数组中只访问1次每个元素,访问后记录到哈希表中,利用空间换时间。于是通过这种方法可以使时间复杂度压缩到O(n)。
class Solution {
/**
* 两数之和问题:给定一个整数数组 nums 和一个整数目标值 target,
* 请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
* 你可以假设每种输入只会对应一个答案,且数组中同一个元素在答案里不能重复出现。
*
* 人话:
* 其实就是要检测数对,因为对于某一个数而言我们有明确的目标,
* 所以我们可以通过哈希表的方式进行前值的存储,便于后值的查询。
*
* @param nums 输入的整数数组
* @param target 目标和值
* @return 两个整数的索引组成的数组,按出现顺序返回(前一个索引小于后一个)
*/
public int[] twoSum(int[] nums, int target) {
int n = nums.length; // 获取数组长度,用于循环遍历
// 创建哈希表,键为数组元素值,值为该元素对应的索引,用于快速查找已遍历过的元素
Map<Integer, Integer> mp = new HashMap<>();
// 遍历数组中的每个元素
for (int i = 0; i < n; ++i) {
int x = nums[i]; // 将当前元素赋值给变量x,减少数组访问次数,优化性能
// 计算目标值与当前元素的差值,即需要找到的另一个元素值
int complement = target - x;
// 检查哈希表中是否存在该差值:若存在,说明之前已遍历过该元素,直接返回两个索引
if (mp.containsKey(complement)) {
return new int[] { mp.get(complement), i };
}
// 若哈希表中不存在该差值,则将当前元素及其索引存入哈希表,供后续元素查找
mp.put(x, i);
}
// 题目假设存在唯一解,此处为默认返回(实际不会执行到)
return new int[] { -1, -1 };
}
}
时间复杂度O(),空间复杂度(O(n))