文章背景图

LeetCode热题100(简单/中等)—— 哈希

2026-02-02
2
-
- 分钟

1 两数之和

难度:简单

题目描述

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 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

  • 只会存在一个有效答案

 

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

思路 - 初见

空间换时间,哈希存储,键为nuns[i] ,值为i ,即下标,遍历数组 ,检查哈希表中target - nums[i]的值是否存在,若存在,返回结果

代码 - 初见

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // Key: 数值, Value: 对应的下标
        Map<Integer, Integer> map = new HashMap<>();
        
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
            int complement = target - nums[i];
            
            // 检查差值是否已经在哈希表中
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i };
            }
        }
        
        // 如果没有找到匹配项,返回空数组
        return new int[0]; 
    }
}

问题

  • 存在自己加自己的问题。

    • 解决:写入同一个循环,边检查边写入。

代码 - 改进

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // Key: 数值, Value: 对应的下标
        Map<Integer, Integer> map = new HashMap<>();
        
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            
            // 检查差值是否已经在哈希表中
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i };
            }
            
            // 如果不在,将当前数值和索引存入表中
            map.put(nums[i], i);
        }
        
        // 如果没有找到匹配项,返回空数组
        return new int[0]; 
    }
}

2 字母异位词分组

难度:中等

题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

 

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]

输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

解释:

  • strs 中没有字符串可以通过重新排列来形成 "bat"

  • 字符串 "nat""tan" 是字母异位词,因为它们可以重新排列以形成彼此。

  • 字符串 "ate" ,"eat" 和 "tea" 是字母异位词,因为它们可以重新排列以形成彼此。

示例 2:

输入: strs = [""]

输出: [[""]]

示例 3:

输入: strs = ["a"]

输出: [["a"]]

 

提示:

  • 1 <= strs.length <= 104

  • 0 <= strs[i].length <= 100

  • strs[i] 仅包含小写字母

思路 - 初见

  • 采用哈希存储,键为组成字符串的字符组,值为组成的字符串组

  • 存储键时需要将字母排序,暂时没有想到改进方法

  • 输出时输出值对应的数组即可

代码 - 初见

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {

        // 创建哈希表:Key 存储排序后的“标准”字符串,Value 存储对应的异位词列表
        Map<String, List<String>> hashMap = new HashMap<>();

        for (String str : strs) {
            // 1. 将当前字符串转换为字符数组
            char[] chars = str.toCharArray();
            
            // 2. 对字符进行排序
            Arrays.sort(chars);
            
            // 3. 将排序后的字符数组转回字符串,作为 HashMap 的 Key
            String str_s = String.valueOf(chars);

            // 4. 判断逻辑:如果 Key 已存在,直接获取 List 并添加;否则创建新 List
            if (hashMap.containsKey(str_s)) {
                // 已有该组,直接追加
                hashMap.get(str_s).add(str);
            } else {
                // 新的组,初始化后再存入
                List<String> list = new ArrayList<>();
                list.add(str);
                hashMap.put(str_s, list);
            }
        }

        // 5. 利用 HashMap 的 values() 方法构造最终的嵌套列表
        return new ArrayList<>(hashMap.values());
    }
}

问题

  • 可以加入边界条件检查

代码 - 改进

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        // 检查边界条件
        if (strs == null || strs.length == 0) {
            return new ArrayList<>();
        }

        // 创建哈希表:Key 存储排序后的“标准”字符串,Value 存储对应的异位词列表
        Map<String, List<String>> hashMap = new HashMap<>();

        for (String str : strs) {
            // 1. 将当前字符串转换为字符数组
            char[] chars = str.toCharArray();
            
            // 2. 对字符进行排序
            Arrays.sort(chars);
            
            // 3. 将排序后的字符数组转回字符串,作为 HashMap 的 Key
            String str_s = String.valueOf(chars);

            // 4. 判断逻辑:如果 Key 已存在,直接获取 List 并添加;否则创建新 List
            if (hashMap.containsKey(str_s)) {
                // 已有该组,直接追加
                hashMap.get(str_s).add(str);
            } else {
                // 新的组,初始化后再存入
                List<String> list = new ArrayList<>();
                list.add(str);
                hashMap.put(str_s, list);
            }
        }

        // 5. 利用 HashMap 的 values() 方法构造最终的嵌套列表
        return new ArrayList<>(hashMap.values());
    }
}

3 最长连续序列

难度:中等

题目描述

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

 

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

示例 3:

输入:nums = [1,0,1,2]
输出:3

 

提示:

  • 0 <= nums.length <= 105

  • -109 <= nums[i] <= 109

思路 - 初见

  • 依旧哈希,先存再找,这次只需存值

  • 先遍历存储到哈希表中

  • 不断查找下一个数是否存在记录长度——若数字之前还有序列会浪费时间

  • 先检查数字的前一个数是否存在,若存在,直接跳过

  • 若长度超过length/2,必为最长(这题貌似用不上)

代码 - 初见

class Solution {
    public int longestConsecutive(int[] nums) {
        // 边界判断
        if (nums.length == 0) return 0;

        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            // 将数字存入set
            set.add(nums[i]);
        }

        int maxLength = 0;
        
        for (int i = 0; i < nums.length; i++) {
            // 非数字起点,直接跳过
            if (set.contains(nums[i] - 1)) {
                continue;
            }

            int length = 1;
            int start = nums[i];
            while (set.contains(start + 1)) {
                start++;
                length++;
            }
            
            if (length > maxLength) {
                maxLength = length;
            }
        }
        return maxLength;
    }
}

问题

  • 在处理超大数组时,出现了超时问题

    • 改进:第二次遍历哈希集合而非数组,减少重复遍历问题

  • 一个连续字段遍历结束时,start - 起点即为长度,可以减少length记录

代码 - 改进

class Solution {
    public int longestConsecutive(int[] nums) {
        // 边界判断
        if (nums == null || nums.length == 0) return 0;

        // 1. 去重并提供 O(1) 查询
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
        int maxLength = 0;
        // 2. 改进点:遍历 set 而不是 nums
        // 这样可以确保每个数字及其逻辑只被处理一次,不受原数组重复项干扰
        for (int num : set) {
            if (!set.contains(num - 1)) {
                int start = num + 1;
                while (set.contains(start)) {
                    start++;
                }
                maxLength = Math.max(maxLength, start - num);
            }
        }
        return maxLength;
    }
}

评论交流

文章目录