LeetCode 1. 两数之和 | 从暴力到哈希表的优化之路

作为 LeetCode 的第一道入门题,「两数之和」虽然标注为简单 难度,却是理解数组遍历与哈希表核心思想的经典案例。它不仅是算法新手的入门必修课,更能让我们体会到 空间换时间 的优化思维。今天从最基础的暴力解法开始,一步步拆解到时间复杂度 O (n) 的最优解法,带你彻底搞懂这道题的解题逻辑~

📖 题目重述

给你一个整数数组 nums 和一个目标值 target,请在数组中找出两个不同位置 的整数,使它们的和恰好等于 target,最后返回这两个数的下标。这里有几个关键规则要注意:

  1. 每个输入都只有一个正确答案
  2. 数组中的同一个元素不能被重复使用;
  3. 返回的下标顺序没有要求。

举个例子,输入数组 [2,7,11,15]、目标值 9,因为 2+7=9,所以返回下标 [0,1] 即可。

🚶 阶梯思路拆解

第一步:暴力枚举所有可能 🥾

要找两个数的和等于目标值,最直观的想法就是把数组中所有数两两配对,逐一检查和是否符合要求。这就是暴力解法的核心逻辑,也是最容易想到的思路。

💡 核心逻辑

用两层嵌套的 for 循环遍历数组:

  • 外层循环固定一个数 nums[i],遍历范围是数组的每一个元素;
  • 内层循环遍历 i 之后的所有数 nums[j](避免重复检查同一对数字);
  • nums[i] + nums[j] == target,直接返回下标数组 {i, j}

✅ 代码实现(Java)

ini 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        // 外层循环固定第一个数
        for (int i = 0; i < n; i++) {
            // 内层循环遍历第二个数,从i+1开始避免重复
            for (int j = i + 1; j < n; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        // 题目保证有解,此处仅为语法兜底
        return new int[0];
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums1 = {2,7,11,15};
        int[] res1 = solution.twoSum(nums1, 9);
        System.out.println(res1[0] + "," + res1[1]); // 输出0,1

        int[] nums2 = {3,2,4};
        int[] res2 = solution.twoSum(nums2, 6);
        System.out.println(res2[0] + "," + res2[1]); // 输出1,2
    }
}

⚙️ 复杂度分析

复杂度类型 计算结果 说明
时间复杂度 O(n²) 两层嵌套循环,最坏情况下需要遍历 n*(n-1)/2 次,n 为数组长度
空间复杂度 O(1) 仅使用了几个临时变量,未开辟额外的数组或集合空间

🚫 遇到的问题

暴力解法的逻辑简单,但效率极低。当数组长度 n 达到 10⁴ 时,n² 就是 10⁸ 次运算,会触发 LeetCode 的超时判定;如果是百万级别的数组,这种方法更是完全不可行。问题的核心在于:内层循环的查找过程重复且耗时,我们需要找到一种更高效的查找方式。

第二步:用哈希表优化查找效率 🗺️

既然暴力解法的痛点是 查找补数 的过程太慢,那我们可以用 哈希表(HashMap)来记录已经遍历过的数字和它的下标,把查找操作从 O (n) 降到 O (1)。这是算法优化中典型的 空间换时间 策略。

💡 核心逻辑

  1. 初始化一个 HashMap,键存储遍历过的数字 ,值存储该数字对应的数组下标
  2. 遍历数组时,对当前数字 nums[i],计算它的 补数 complement = target - nums[i]
  3. 检查补数是否存在于 HashMap 中:
    • 若存在,说明之前已经遍历过这个补数,直接返回 {map.get(complement), i}
    • 若不存在,将当前数字和下标存入 HashMap,继续遍历下一个元素。

📊 图文演示(以 nums=[2,7,11,15], target=9 为例)

我们一步步拆解 HashMap 的变化和查找过程,直观感受优化后的逻辑(如图所示):

  1. 遍历 i=0,nums [i]=2,补数 = 9-2=7。此时 HashMap 为空,将 2→0 存入 Map:

    ini 复制代码
    HashMap:{2=0}
    当前遍历:nums[0]=2 → 补数7不存在 ❌
  2. 遍历 i=1,nums [i]=7,补数 = 9-7=2。检查 HashMap,发现 2 存在且对应下标 0:

    ini 复制代码
    HashMap:{2=0}
    当前遍历:nums[1]=7 → 补数2存在 ✅ → 返回{0,1}

✅ 代码实现(Java)

ini 复制代码
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 初始化HashMap,键:数字,值:下标
        Map<Integer, Integer> numMap = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            int complement = target - nums[i];
            // 检查补数是否在HashMap中
            if (numMap.containsKey(complement)) {
                return new int[]{numMap.get(complement), i};
            }
            // 若不存在,将当前数字和下标存入Map
            numMap.put(nums[i], i);
        }
        // 题目保证有解,此处仅为语法兜底
        return new int[0];
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums1 = {2,7,11,15};
        int[] res1 = solution.twoSum(nums1, 9);
        System.out.println(res1[0] + "," + res1[1]); // 输出0,1

        int[] nums2 = {3,3};
        int[] res2 = solution.twoSum(nums2, 6);
        System.out.println(res2[0] + "," + res2[1]); // 输出0,1
    }
}

⚙️ 复杂度分析

复杂度类型 计算结果 说明
时间复杂度 O(n) 仅需遍历一次数组,HashMap 的查找和插入操作均为 O (1)
空间复杂度 O(n) 最坏情况下,需要将数组中 n 个元素全部存入 HashMap

✨ 优化亮点

这种方法通过 HashMap 将原本 O (n) 的查找过程优化为 O (1),时间复杂度从 O (n²) 骤降到 O (n),虽然牺牲了部分空间,但在实际开发中,时间效率的提升往往更重要。这也是「两数之和」这道题的最优解法

📝 总结

「两数之和」看似简单,却蕴含了算法优化的核心思维:用合适的数据结构解决性能瓶颈 。我们从暴力枚举的基础思路出发,找到 重复查找 的性能问题,再通过哈希表实现高效查找,这个优化路径也是很多数组类算法题的通用解题模板。

同类题扩展建议

掌握了这道题的哈希表思路后,可以继续练习这些进阶题目,巩固优化思维:

  1. LeetCode 15. 三数之和:在两数之和的基础上增加一层循环,结合双指针优化去重;
  2. LeetCode 18. 四数之和:延续三数之和的思路,注意边界条件和去重逻辑;
  3. LeetCode 454. 四数相加 II:用两个 HashMap 分别存储两组数的和,进一步降低时间复杂度。

算法学习不是一蹴而就的,从基础解法开始逐步优化,才能真正理解背后的逻辑。希望这篇文章能帮你吃透「两数之和」,也为后续的算法学习打下坚实的基础~

📢 关注不迷路,算法学习更高效!

如果这篇拆解对你有帮助,别忘了关注我的微信公众号【小镇冥想人】 ~ 后续会持续更新 LeetCode 高频题的阶梯式解题思路,从暴力到最优解层层递进,每道题都搭配详细图文和代码注释,帮你轻松攻克算法难关。

相关推荐
one____dream17 小时前
【算法】移除链表元素与反转链表
数据结构·python·算法·链表
memmolo17 小时前
【3D测量中的术语:系统误差、随机误差、精密度、准确度】
算法·计算机视觉·3d
睡不醒的kun17 小时前
不定长滑动窗口-基础篇(2)
数据结构·c++·算法·leetcode·哈希算法·散列表·滑动窗口
霑潇雨17 小时前
题解 | 分析每个商品在不同时间段的销售情况
数据库·sql·算法·笔试
金枪不摆鳍17 小时前
算法-动态规划
算法·动态规划
季明洵17 小时前
Java中哈希
java·算法·哈希
jaysee-sjc17 小时前
【练习十】Java 面向对象实战:智能家居控制系统
java·开发语言·算法·智能家居
cici1587417 小时前
基于MATLAB实现eFAST全局敏感性分析
算法·matlab
gihigo199817 小时前
MATLAB实现K-SVD算法
数据结构·算法·matlab
dyyx11117 小时前
C++编译期数据结构
开发语言·c++·算法