同样是 Two Sum,暴力解法耗时 56ms,而哈希解法仅需 2ms,差距在哪?
话不多说,先上图
下图为暴力解法

图1-1
下图为暴力解法的代码
js
public class TwoSum {
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[i] + nums[j] == target) {
// 找到则返回两个元素的下标
return new int[]{i, j};
}
}
}
// 题目假设一定有解,此处可根据实际需求返回null或抛出异常
return null;
}
}
下图为借助HashMap实现的

图1-3
下图为借助**HashMap**实现的代码
class
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer>sumMap=new HashMap<>();
//这里第一个Integer是nums[i]
//第二个Integer是i(数组的索引值)
for(int i=0;i< nums.length;i++){
//这里的complement指的是目标值和当前遍历的值的差值
int complement=target-nums[i];
if(sumMap.containsKey(complement)){
return new int[]{sumMap.get(complement),i};
}
//如果在sumMap中没有找到与nums[i]和为target的值
// 那就把当前的值和对应的索引放在sumMap里,
//为了给后面还没有遍历到的元素做匹配存储
sumMap.put(nums[i],i);
}
return null;
}
}
作为"小趴菜"的我第一眼看到这个题目,想到的就是暴力解法 。
这种方法容易想到而且好理解,但总觉得不对劲------如果数组很长,这代码岂不是要跑半天?比如说当数组长度 n=10⁵ 时,暴力解法需要计算 10¹⁰ 次,直接超时!而且效率极低!所以此方法绝不是最佳。其实这题藏着哈希表最核心的用法:用空间换时间,把"找元素"的效率从O(n)压到O(1)。
暴力解法 和利用HashMap解法的主要区别:
想找和为target的另一个数,其实就是找target - nums[i]。暴力法是一个个问(遍历),而哈希表像个"通讯录"------把见过的数和它的下标记下来,下次需要时直接查(不用再遍历)。比如 nums = [2,7,11,15],target=9:
-
当i=0(nums[i]=2),需要找9-2=7,查通讯录(目前为空),就把2和下标0存进去;
-
当i=1(nums[i]=7),需要找9-7=2,查通讯录------哎,2在里面!直接返回[0,1]。
-
图1-4中 map.containsKey(complement) :"这一步就是'查通讯录',时间复杂度O(1)";
下表展示两种解法复杂度区别以及使用场景
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
暴力循环 | O(n²) | O(1) | 数组极短的情况(n<1k) |
哈希表 | O(n) | O(n) | 大部分场景(n>1w) |
除了这两种解法,是否还有其他解法。
答案是肯定的
它就是"双指针解法"
但是使用这种解法是有前提的。已排序数组 是双指针优化的关键前提!如果题目给定的数组是升序排列 (如 [2, 7, 11, 15]
),可以直接用双指针将时间复杂度优化到 O(n) ,且空间复杂度为 O(1)(无需额外哈希表)。
上代码:
`public int[] twoSum(int[] nums, int target) {
sql
int left = 0, right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
return new int[]{left, right}; // 返回索引
} else if (sum < target) {
left++; // 和太小,左指针右移
} else {
right--; // 和太大,右指针左移
}
}
return null; // 无解
}`
- 时间复杂度 :O(n)
- 双指针最多遍历整个数组一次(如最坏情况下
left
和right
相遇)。
- 双指针最多遍历整个数组一次(如最坏情况下
- 空间复杂度 :O(1)
- 仅用两个指针变量,无额外空间。
以下是三种解法复杂度表
解法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
暴力双重循环 | O(n²) | O(1) | 无序数组,数据量小(n<1k) |
哈希表 | O(n) | O(n) | 无序数组,数据量大 (n>1w) |
双指针 | O(n) | O(1) | 已排序数组 |
一定要注意!!!使用双指针的前提是已排序的数组
最后关于三数之和的复杂度,想和掘友们聊聊:暴力是O(n³),排序+双指针是O(n²) ,理论上哈希表也能做(存两数和),但实际为啥很少用哈希法?是空间换时间不划算,还是去重逻辑太复杂?大家在项目里遇到类似三数问题,优先选哪种思路?
有任何疑问,或者有其他优化思路,欢迎在评论区讨论~
欢迎大佬们不吝赐教~
感谢观看!