leetcode-hot100-哈希表:1两数之和-49字母异位词分组-128最长连续序列

哈希表基础

解决的问题:一般哈希表都是用来快速判断一个元素是否出现集合里。

哈希碰撞:拉链法和线性探测法。

常见的三种哈希结构:数组,集合set,映射map

Hashmap的用法:键值对存储。键唯一,值可重复。允许一个 null 键和多个 null 值。不保证顺序(Java 8 后按插入顺序大致保持)

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

// 创建
Map<String, Integer> map = new HashMap<>();

// 添加/更新元素
map.put("apple", 1);        // 添加
map.put("apple", 2);        // 更新
map.put("banana", 3);
map.put("cherry", 4);

// 获取元素
int value = map.get("apple");      // 2
Integer val = map.get("orange");   // null(不存在)

// 检查键是否存在
boolean hasApple = map.containsKey("apple");    // true
boolean hasOrange = map.containsKey("orange");  // false

// 遍历
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 获取键集合、值集合
Set<String> keys = map.keySet();
Collection<Integer> values = map.values();

// 删除
map.remove("apple");

HashSet的用法:去重集合,快速判断元素是否存在

java 复制代码
import java.util.HashSet;
import java.util.Set;

Set<Integer> set = new HashSet<>();

// 添加元素
set.add(1);
set.add(2);
set.add(3);
set.add(1);  // 重复,不会添加

// 检查元素是否存在
boolean contains = set.contains(2);  // true

// 删除元素
set.remove(2);

// 遍历
for (int num : set) {
    System.out.println(num);
}

// 获取大小
int size = set.size();

// 清空
set.clear();

LinkedHashMap(保持插入顺序):适合需要顺序的 LRU 缓存实现。

java 复制代码
import java.util.LinkedHashMap;
import java.util.Map;

Map<String, Integer> map = new LinkedHashMap<>();

// 添加元素
map.put("z", 1);
map.put("a", 2);
map.put("b", 3);

// 遍历会按插入顺序
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey());  // 输出顺序:z, a, b
}

1两数之和

题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。可以按任意顺序返回答案。

这个题的思路是,利用hashmap来存已经遍历过的数组元素,比如我遍历到了元素3,target=9,那么我就要找,hashmap里面是否已经添加过target-3这个元素。如果已经有,那么就返回这两个的下表。

这题要想清楚四个点:

为什么会想到用哈希表?什么时候使用哈希法,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候。这里也就是要找target-3这个元素。

哈希表为什么用map?本题不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放。

本题map是用来存什么的?map用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)

map中的key和value用来存什么的?key存数组元素,value存数组下标。因为本题给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标,下标是要寻找的东西。

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result=new int[2];
        Map<Integer,Integer> map= new HashMap<>(); //存遍历过的数组元素及其下标

        if(nums == null || nums.length == 0){
            return result;
            }

        for(int i=0;i<nums.length; i++){
            int temp=target-nums[i];
            if(map.containsKey(temp)){
                result[0]=i;
                result[1]=map.get(temp);
                break;
            }
            map.put(nums[i],i);
        }

        return result;
    }
}

之前没有注意过map.put(nums[i],i);和if的位置关系。如果先put再if会有一个元素用两次的问题。

49字母异位词分组

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

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

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

解释:在 strs 中没有字符串可以通过重新排列来形成 "bat"。

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

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

这个题在242有效的字母异位词做过一个简单版本的。

核心思想:利用每个字符串的字符计数来生成一个唯一的 key,然后将签名相同的字符串放在同一个列表中。

java 复制代码
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> map=new HashMap<String,List<String>>();
        for(String str:strs){

            int[] counts=new int[26];
            int len=str.length();
            for(int i=0;i<len;i++){ // 遍历当前字符串的每个字符,统计每个字母出现的次数
                counts[str.charAt(i)-'a']++;
            }

            StringBuffer sb=new StringBuffer(); // 使用 StringBuffer 来高效地拼接字符串,因为字符串拼接操作较多
            for(int i=0;i<26;i++){// 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
                if(counts[i]!=0){
                    sb.append((char)('a'+i));
                    sb.append(counts[i]);
                }
            }
            String key=sb.toString(); //拼接后作为key
            List<String> list=map.getOrDefault(key,new ArrayList<String>()); //key对应的是所有异位词的list
            list.add(str);
            map.put(key,list);
        }
         return new ArrayList<List<String>>(map.values());
    }
}

过程模拟:生成完key之后是怎么处理的。

String[] strs = {"eat", "tea", "tan"};

第 1 步:处理第一个字符串 "eat"

统计字符次数得到 counts:a:1, e:1, t:1。

拼接成 key:"a1e1t1"。

执行 List list = map.getOrDefault(key, new ArrayList());

此时 map 为空,所以 getOrDefault 返回一个新创建的 ArrayList(假设叫 list1)。

list.add(str) → list1 现在包含 ["eat"]。

map.put(key, list) → 将 key="a1e1t1" 与 list1 放入 map。

此时 map 的内容为:{ "a1e1t1" : ["eat"] }

第 2 步:处理第二个字符串 "tea"

统计字符次数:a:1, e:1, t:1,生成的 key 同样是 "a1e1t1"。

执行 List list = map.getOrDefault(key, new ArrayList());

因为 map 中已存在 key="a1e1t1",所以 getOrDefault 返回之前存储的列表,即 ["eat"](我们称它为 list1 的引用)。

list.add(str) → 向这个列表中添加 "tea",现在该列表变为 ["eat", "tea"]。

map.put(key, list) → 将 key="a1e1t1" 与更新后的列表(仍然是同一个列表对象)重新放入 map。

此时 map 的内容更新为:{ "a1e1t1" : ["eat", "tea"] }

第 3 步:处理第三个字符串 "tan"

统计字符次数:a:1, n:1, t:1,生成的 key 为 "a1n1t1"(与之前不同)。

执行 List list = map.getOrDefault(key, new ArrayList());

map 中没有 "a1n1t1",所以返回一个新创建的 ArrayList(叫 list2)。

list.add(str) → list2 添加 "tan",变为 ["tan"]。

map.put(key, list) → 将 key="a1n1t1" 与 list2 放入 map。

最终 map 的内容:

{ "a1e1t1" : ["eat", "tea"], "a1n1t1" : ["tan"] }

语法知识补充:

StringBuffer

StringBuffer 是一个可变的字符序列,适合在循环中频繁拼接字符串。相比直接用 + 拼接(每次都会创建新的字符串对象),StringBuffer 效率更高。这里先用 append 方法逐个添加字符和数字,最后调用 toString() 得到最终的 String 对象作为 key。

map.getOrDefault(key, defaultValue)

这是一个方便的方法:如果 map 中存在 key,则返回对应的 value;否则返回指定的默认值(这里是一个新的 ArrayList)。这样可以避免先判断 containsKey 再手动创建列表的繁琐步骤。

map.put(key, list)

将 key 和对应的列表存入 map。如果 key 已经存在,新的 list 会覆盖旧的,但由于我们是从 map 中取出原来的 list 并添加了元素,再 put 回去,所以实际上更新了 map 中该键对应的列表内容。

return new ArrayList<List>(map.values())

map.values() 返回 map 中所有 value 的集合(Collection<List>)。通过 ArrayList 的构造方法,可以将这个集合转换成一个新的 ArrayList 并返回。注意这里使用了泛型,ArrayList<List> 表示这个列表的元素类型是 List。

128最长连续序列

题目:给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:输入:nums = [100,4,200,1,3,2],输出:4

解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

使用哈希集合

java 复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> st=new HashSet<>();
        for(int num:nums){
            st.add(num);
        }

        int ans=0;
        for(int x:st){
            if(st.contains(x-1)){
                continue;
            }

            int y=x+1;
            while(st.contains(y)){
                y++;
            }
            ans=Math.max(ans,y-x);
        }
        return ans;
    }
}
相关推荐
代码探秘者2 小时前
【Redis】双写一致性:延迟双删 / 读写锁 / 异步通知 / Canal,一文全解
java·数据库·redis·后端·算法·缓存
小指纹2 小时前
2026牛客寒假算法基础集训营1
算法·macos·cocoa
郭逍遥2 小时前
[Godot] 沃罗诺伊图生成算法
算法·c#·游戏引擎·godot
像污秽一样2 小时前
算法设计与分析-算法效率分析基础-蛮力法
数据结构·算法·排序算法
祁同伟.2 小时前
【算法】优选 · 双指针
c++·算法·容器·stl
项目申报小狂人2 小时前
基于迁移学习与丢弃法的神经网络算法在无人机失移动目标搜索中的应用,含代码
神经网络·算法·迁移学习
stolentime2 小时前
洛谷P15652 [省选联考 2026] 排列游戏 / perm题解
c++·算法·交互·洛谷·联合省选2026
仰泳的熊猫2 小时前
题目1834:蓝桥杯2016年第七届真题-路径之谜
数据结构·c++·算法·蓝桥杯·深度优先·图论
机器学习之心2 小时前
198种组合算法+优化SVR支持向量机回归+SHAP分析+新数据预测!机器学习可解释分析,强烈安利,粉丝必备!
算法·shap分析·新数据预测·优化svr支持向量机回归