精选专栏链接 🔗
欢迎订阅,点赞+关注,每日精进1%,与百万开发者共攀技术珠峰
更多内容持续更新中~
【LeetCode 热题 100】两数之和--- 暴力法与哈希表法详解
在算法面试和刷题的起步阶段,"两数之和"绝对是绕不开的经典入门题。它不仅是 LeetCode 热题 100 的第一题,更是理解"空间换时间"这一核心算法思想的最佳案例。今天我们就用 Java 来深度剖析这道题的两种主流解法。
📝题目描述
给定一个整数数组
nums和一个整数目标值target,请你在该数组中找出 和为目标值target的那 两个 整数,并返回它们的数组下标。
- 假设:每种输入只会对应一个答案。
- 限制:数组中同一个元素在答案里不能重复出现。
示例:
java
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
📊题目要求
-
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
-
你可以按任意顺序返回答案。
💡提示信息
- 2 <= nums.length <= 104
- -109 <= nums[i] <= 109
- -109 <= target <= 109
- 只会存在一个有效答案
解法一:暴力枚举法
这是最直观、最容易想到的思路。既然要找两个数的和等于
target,那我们就通过两层循环遍历所有可能的数字组合。直到找到符合要求的两个数字组合,返回其下标。
核心逻辑:
- 第一层循环固定基准数 :我们需要遍历数组中的每一个元素,将其作为配对的第一个数
(基准数 x),其索引为i。外层循环从数组的第一个元素一直走到倒数第二个元素即可。 - 第二层循环寻找搭档 :从
i+1开始选取第二个数y(索引为j),避免使用同一个元素。 - 条件判断与返回 :在内层循环中,我们将基准数 nums[i] 与当前遍历到的搭档 nums[j] 相加。一旦发现和正好等于 target,说明找到了唯一解,立刻将这两个数的索引 [i, j] 封装并返回。由于题目保证有且仅有一个有效答案,程序在此处结束即可。
暴力枚举法 Java 代码实现:
java
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
for(int i = 0; i < n-1; i++){ // 第一层循环
for(int j = i + 1; j < n; j++){ // 第二层循环,从 i+1 开始,避免重复和自身相加
if(nums[i] + nums[j] == target){
return new int[]{i,j};
}
}
}
return new int; // 题目保证有解,此处仅为语法完整
}
}
提交代码,运行结果如下:

- 执行用时分析 :
击败 23.06%的提交者。代码跑赢了大约四分之一的提交者,但有接近 77% 的人更快; - 消耗内存分析 :
击败 35.61%的提交者。内存表现中规中矩,比三分之一的人省内存,但依然处于下游水平。
复杂度分析:
- 时间复杂度 : O ( N 2 ) O(N^2) O(N2)。最坏情况下,我们需要遍历几乎所有的元素对,当数据量较大时效率较低。
- 空间复杂度 : O ( 1 ) O(1) O(1)。只使用了常数个变量存储索引。
显然,暴力枚举法性能表现并不好,可以考虑使用性能更好的哈希表法解决本题。
解法二:哈希表法 (Hash Map) --- 推荐解法
暴力法之所以慢,是因为暴力法在找搭档时,总是在"翻旧账"------每遇到一个人,都要回头把前面所有人重新看一遍,问:"你们谁适合我?"。
而哈希表法的核心思想是:"过目不忘,一次通过"。我们不回头,只向前看,并且把遇到的人记在"小本本"(哈希表)上。
我们将原来的"两层循环"简化为"一层循环"。在这个过程中,我们维护一个哈希表(在 Java 中通常用 HashMap),用来充当那个"小本本"。这样就可以把时间复杂度降低到 O ( 1 ) O(1) O(1)。
核心逻辑:
创建一个哈希表,用来存储<数值, 索引>的键值对。键(Key)存数值本身,值(Value)存它在数组中的下标索引。 在遍历数组的过程中,将元素的值和它的索引存入哈希表。
- 启动一个
for 循环,从头到尾依次取出数组中的每个元素 nums[i]; - 对于当前手里的数字 nums[i],我们要立刻算出:为了达到 target,我还需要哪个数字(此数字可表示为complement)?例如:目标是 9,手里是 2,那我脑子里立刻反应出我需要找 7。
- 我们在哈希表中查询
是否存在键为 complement 的记录; - 如果
哈希表中有complement元素,说明我们找到了完美的搭档!那个搭档的索引可以直接从哈希表中取出来,而当前数字的索引就是 i。此时直接返回这两个索引即可; - 如果
哈希表中没有complement元素,说明当前这个 nums[i]暂时没有搭档,它只能先"落单"。此时,我们将 nums[i] 作为键,i 作为值,存入哈希表。这一步的意义是为了服务未来的元素---也许后面的某个数字,正好需要现在的 nums[i] 来配对。
Java 代码实现:
java
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
// 创建一个哈希表,以数组内元素的值为 key,以数组内元素的索引为 value
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
int complement = target - nums[i]; // 计算需要寻找的补数
if(map.containsKey(complement)){
return new int[]{i, map.get(complement)};
} else {
map.put(nums[i], i);
}
}
return new int[] {};
}
}
提交代码,运行结果如下:

- 执行用时分析 :
击败99.69%的提交者。执行只花费2ms,这是一个顶尖水平的成绩; - 消耗内存分析 :
击败 61.38%的提交者。这是一个中规中矩的成绩,属于典型的"空间换时间"。
复杂度分析:
- 时间复杂度 : O ( N ) O(N) O(N)。我们只需要遍历一次数组,哈希表的插入和查找操作平均时间复杂度为 O ( 1 ) O(1) O(1)。
- 空间复杂度 : O ( N ) O(N) O(N)。主要取决于哈希表中存储的元素数量。
总结对比
| 方法 | 时间复杂度 | 空间复杂度 | 评价 |
|---|---|---|---|
| 暴力枚举 | O ( N 2 ) O(N^2) O(N2) | O ( 1 ) O(1) O(1) | 简单易懂,但性能较差,适合小规模数据 |
| 哈希表法 | O ( N ) O(N) O(N) | O ( N ) O(N) O(N) | 最优解,典型的"空间换时间"策略 |
希望这篇解析能帮你彻底搞懂 "两数之和"!如果你觉得有帮助,记得点赞收藏哦!更多内容持续更新中~~~