【力扣热题100学习笔记】 - 哈希

力扣热题100学习笔记 - 哈希

此博客用于记录刷题过程中的思路与代码,方便日后回顾。

每道题我会先自己尝试,再参考题解优化,欢迎交流指正。


目录

  1. 哈希篇
    • [1. 两数之和](#1. 两数之和)
    • [49. 字母异位词分组](#49. 字母异位词分组)
    • [128. 最长连续序列](#128. 最长连续序列)

哈希篇

哈希表的核心思想:用空间换时间,将查找时间从 O(n) 降到 O(1)。


1. 两数之和

题目描述

给定一个整数数组 nums 和一个目标值 target,请找出数组中和为目标值的两个整数,返回它们的数组下标。

示例

复制代码
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:nums[0] + nums[1] = 2 + 7 = 9

思路

  • 核心问题:如何快速找到与当前数匹配的另一个数?暴力法需要 O(n²),用哈希表优化查找过程。
  • 哈希表优化 :遍历数组时,用 HashMap 存储已经遍历过的元素(key 为元素值,value 为下标)。对于当前元素 nums[i],计算所需的补数 complement = target - nums[i],如果补数已在哈希表中,说明找到了答案。
  • 关键细节 :先判断补数是否存在,再将当前元素存入哈希表。这样可以避免同一个元素被使用两次(例如 target = 6, nums = [3, 3],第一个 3 存入前 map 为空,判断时找不到,存入;第二个 3 判断时 map 中有第一个 3,成功匹配)。

复杂度

  • 时间:O(n),只需遍历一次数组,哈希表的查找和插入都是 O(1)
  • 空间:O(n),最坏情况下需要存储 n 个元素

Java代码

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        if (nums == null || nums.length == 0) {
            return res;
        }
        
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                res[0] = map.get(complement);
                res[1] = i;
                return res;
            }
            map.put(nums[i], i);
        }
        return res;
    }
}

易错点/注意

  • 返回下标顺序 :题目没有要求顺序,但通常返回 [较小下标, 较大下标],这里先找到的补数下标一定小于当前下标
  • 同一元素不能用两次 :先判断再存入,避免当前元素和自己匹配(如 target=4, nums=[2] 不会误判)
  • 边界处理:数组为空或长度为 0 时直接返回空数组
  • 找到后及时返回:找到答案后立即返回,不需要继续遍历

49. 字母异位词分组

题目描述

给定一个字符串数组,将字母异位词(由相同字母重排组成的词)组合在一起。

示例

复制代码
输入:strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:[["bat"],["nat","tan"],["ate","eat","tea"]]

核心思路:用字符频次数组作为 key,将字母异位词归为一组

处理流程

  • 遍历每个字符串,统计 26 个字母的出现次数
  • 将频次数组转为字符串作为 HashMap 的 key
  • 相同 key 的字符串属于字母异位词,放入同一列表

返回结果:所有分组的列表

Java代码

java 复制代码
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String, List<String>> map = new HashMap<>();
        ArrayList<List<String>> res = new ArrayList<>();
        
        for (String str : strs) {
            // 生成指纹:统计每个字母出现次数
            char[] num = new char[26];
            char[] chars = str.toCharArray();
            for (char c : chars) {
                num[c - 'a']++;
            }
            String key = new String(num);
            
            // 分组:将当前单词加入对应指纹的列表中
            if (map.containsKey(key)) {
                map.get(key).add(str);
            } else {
                ArrayList<String> list = new ArrayList<>();
                list.add(str);
                map.put(key, list);
                res.add(list);  // 新分组加入结果集
            }
        }
        return res;
    }
}

易错点/注意

  • 指纹生成方式 :用 char[] 而非 int[],这样可以直接 new String(num) 转换为字符串作为 key,简洁高效
  • 结果同步res 中存储的是 map 中列表的引用,不需要额外维护,修改 map 中的列表会自动反映到 res
  • 空字符串处理 :空字符串会生成全为 0 的 char 数组,同样可以正确分组
  • 返回类型 :返回 ArrayList<List<String>> 符合 List<List<String>> 的类型要求

128. 最长连续序列

题目描述

给定一个未排序的整数数组,找出数字连续的最长序列的长度(要求 O(n) 时间复杂度)。

示例

复制代码
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长连续序列是 [1,2,3,4],长度为 4。

思路

  • 用 HashSet 存储所有数,实现 O(1) 的查找。
  • 遍历每个数,只有当它是某个连续序列的起点 (即 num - 1 不存在)时,才开始向后计数。
  • 计数过程中不断查找 num + 1 是否存在,直到断掉,更新最大长度。

复杂度

  • 时间:O(n)
  • 空间:O(n)

Java代码

java 复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
        // 边界条件检查
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        // 创建 HashSet,用于存储所有数字
        // Set<Integer> 表示这是一个存储 Integer 类型的集合
        // new HashSet<>() 创建了一个 HashSet 对象
        Set<Integer> numSet = new HashSet<>();
        
        // 将数组中的所有数字添加到 HashSet 中
        for (int num : nums) {
            numSet.add(num);  // add() 方法添加元素
        }
        
        int maxLength = 0;  // 记录最长序列长度
        
        // 遍历 HashSet 中的每个数字
        for (int num : numSet) {
            // 关键逻辑:只有当 num-1 不存在时,num 才是序列的起点
            // contains() 方法检查元素是否存在,返回 true 或 false
            if (!numSet.contains(num - 1)) {
                int currentNum = num;
                int currentLength = 1;  // 当前序列长度,至少包含自己
                
                // 从起点开始,不断查找下一个连续的数字
                // while 循环会一直执行,直到找不到下一个数字
                while (numSet.contains(currentNum + 1)) {
                    currentNum++;  // 移动到下一个数字
                    currentLength++;  // 序列长度加 1
                }
                
                // 更新最大长度
                // Math.max(a, b) 返回 a 和 b 中较大的那个
                maxLength = Math.max(maxLength, currentLength);
            }
        }
        
        return maxLength;
    }
}

易错点/注意

  • 为什么要判断起点?避免重复计数,每个序列只从最小数开始统计。
  • 注意空数组的边界情况。


小结

题目 核心技巧 时间复杂度 空间复杂度
两数之和 哈希表存差值的下标 O(n) O(n)
字母异位词分组 排序后作为 key O(n×k log k) O(n×k)
最长连续序列 HashSet + 找起点 O(n) O(n)

刷题心得

  • 哈希表类题目通常先想「能不能用 Map/Set 减少一次循环」。
  • 遇到「连续」「子数组」类问题,多考虑 Set 或前缀和结合哈希。
  • 基础薄弱时,先理解暴力解法,再逐步优化,不要一开始就追求最优解。

持续更新中,代码待补充。每道题我会在理解透彻后把代码填进来,并补充更多细节和坑点。

相关推荐
凌波粒2 小时前
LeetCode--349.两个数组的交集(哈希表)
java·算法·leetcode·散列表
于先生吖2 小时前
Java+SpringBoot 无人健身房物联网系统完整源码实现
java·spring boot·物联网
johnrui3 小时前
SpringBoot-JdbcTemplate
java·spring boot·后端
码云社区3 小时前
JAVA二手车交易二手车市场系统源码支持微信小程序+微信公众号+H5+APP
java·开发语言·微信小程序·二手交易·闲置回收
crescent_悦3 小时前
C++:The Largest Generation
java·开发语言·c++
indexsunny3 小时前
互联网大厂Java面试实战:从Spring Boot到微服务的技术问答解析
java·spring boot·redis·微服务·消息队列·电商
SteveSenna4 小时前
项目:Trossen Arm MuJoCo
人工智能·学习·算法
m0_747304164 小时前
GNN学习
学习
希望永不加班4 小时前
SpringBoot 过滤器(Filter)与请求链路梳理
java·spring boot·后端·spring