【LeetCode 热题 100】两数之和— 暴力法与哈希表法详解


精选专栏链接 🔗


欢迎订阅,点赞+关注,每日精进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,那我们就通过两层循环遍历所有可能的数字组合。直到找到符合要求的两个数字组合,返回其下标。

核心逻辑:

  1. 第一层循环固定基准数 :我们需要遍历数组中的每一个元素,将其作为配对的第一个数(基准数 x),其索引为 i。外层循环从数组的第一个元素一直走到倒数第二个元素即可。
  2. 第二层循环寻找搭档 :从 i+1 开始选取第二个数 y(索引为 j),避免使用同一个元素。
  3. 条件判断与返回 :在内层循环中,我们将基准数 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)存它在数组中的下标索引。 在遍历数组的过程中,将元素的值和它的索引存入哈希表。

  1. 启动一个 for 循环,从头到尾依次取出数组中的每个元素 nums[i];
  2. 对于当前手里的数字 nums[i],我们要立刻算出:为了达到 target,我还需要哪个数字(此数字可表示为complement)?例如:目标是 9,手里是 2,那我脑子里立刻反应出我需要找 7。
  3. 我们在哈希表中查询是否存在键为 complement 的记录
  4. 如果哈希表中有complement元素,说明我们找到了完美的搭档!那个搭档的索引可以直接从哈希表中取出来,而当前数字的索引就是 i。此时直接返回这两个索引即可;
  5. 如果哈希表中没有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) 最优解,典型的"空间换时间"策略

希望这篇解析能帮你彻底搞懂 "两数之和"!如果你觉得有帮助,记得点赞收藏哦!更多内容持续更新中~~~

相关推荐
Pointer Pursuit9 小时前
哈希表的实现
数据结构·哈希算法·散列表
Refrain_zc9 小时前
Android开发在线音频播放器之章节三核心PlayerService
java
葫三生9 小时前
开源社区为《论三生原理》系列提供“第二评价体系”?
人工智能·科技·深度学习·算法·机器学习·开源
故事和你919 小时前
洛谷-【动态规划1】动态规划的引入4
开发语言·数据结构·c++·算法·动态规划·图论
用户938515635079 小时前
移除数组里的指定元素,你学会了吗?
javascript·算法
日月云棠9 小时前
17 Dubbo 2.7 集群容错策略源码解析
java·后端
Refrain_zc9 小时前
Android开发纯按键文件浏览器
java
吃好睡好便好9 小时前
创建全0矩阵和全1矩阵
开发语言·学习·线性代数·算法·matlab·信息可视化·矩阵
monkeyhlj9 小时前
Harness理解学习
java·人工智能·python·学习·ai编程