力扣热题100-《哈希》
哈希 即散列 其发挥的作用包括:1.压缩映射 提高存储空间的利用率 2.提高查询效率,快速去重或快速比较
1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
发现nums[i]和target有对应关系(target - nums[i] = nums[j]),可使用哈希提升查询效率
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> hashtable = new HashMap<Integer,Integer>();
for(int i = 0 ; i < nums.length; i++) {
if(hashtable.containsKey(target - nums[i])) {
return new int[]{hashtable.get(target - nums[i]),i};
}
hashtable.put(nums[i],i);
}
return new int[0];
}
}
2.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
1.利用hashmap将排序好的字母作为key 2.把可以映射到数组的str作为value
import java.util.*;
public class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
// 1. 创建 HashMap,key是排序后的字符串,value是异位词列表
Map<String, List<String>> map = new HashMap<>();
// 2. 遍历每个字符串
for (String str : strs) {
// 3. 将字符串转换为字符数组并排序
char[] chars = str.toCharArray();
Arrays.sort(chars);
String key = new String(chars); // 排序后的字符串作为key
if (!map.containsKey(key)) {
map.put(key, new ArrayList<>());
}
map.get(key).add(str);
}
return new ArrayList<>(map.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
1.先用set去重 2.遍历数组,把每一个数作为起点
class Solution {
public int longestConsecutive(int[] nums) {
• Set<Integer> set = new HashSet<>();
• for(int num : nums) {
• set.add(num);
• }
• int max = 0;
• for (int num : set) {
• if(!set.contains( num-1 )) {
• int now = num;
• int count = 1;
• while(set.contains(now+1)) {
• now += 1;
• count += 1;
• }
• max = Math.max(max,count);
• }
}
• return max;
}
}
解析HashMap源码:
Contains和ContainsKey方法:
如图,contains和ContainsKey方法实际调用了getNode方法
首先要对比哈希值,再对比Key值(因为哈希值相同不一定key会相同)
成功找到对应的哈希桶后: 对该哈希桶(必有一个头节点)操作
如果头节点不是目标节点,处理后续节点
if ((e = first.next) != null) {
把头节点的下一个节点赋值给e并判断是否存在后续节点
若存在后续节点(哈希桶的本质是链表或红黑树):差异化查找,链表就正常遍历,红黑树查询时间logn
containsKey(Object key):判断当前 Map 集合中是否包含指定的键(key) ,只关注「键」的存在性,与对应的值(value)无关。
contains(Object o):判断当前 Collection 集合中是否包含指定的元素(element) ,Collection 是单元素集合,没有「键值对」之分,直接判断元素本身是否存在。所有的Colletion类都会实现该方法
常见CRUD操作:
| 方法 | 返回值 | 说明 | 示例 |
|---|---|---|---|
V put(K key, V value) | V | 添加/更新:key存在则覆盖,返回旧值;否则返回null | map.put("a", 1) |
V putIfAbsent(K key, V value) | V | 不存在才添加:key存在则不操作,返回旧值;否则插入并返回null | map.putIfAbsent("a", 2) |
V get(Object key) | V | 查询:key不存在返回null | Integer v = map.get("a") |
V getOrDefault(Object key, V defaultValue) | V | 查询带默认值:不存在返回default而非null | map.getOrDefault("b", 0) |
V remove(Object key) | V | 删除:返回被删的值,不存在返回null | map.remove("a") |
boolean remove(Object key, Object value) | boolean | 条件删除:仅当key-value匹配时才删除 | map.remove("a", 1) |
V replace(K key, V value) | V | 替换:key存在则替换,返回旧值;不存在返回null(不插入) | map.replace("a", 2) |
boolean replace(K key, V oldValue, V newValue) | boolean | 条件替换:仅当旧值匹配时才替换 | map.replace("a", 1, 2) |
boolean containsKey(Object key) | boolean | 是否包含key | map.containsKey("a") |
boolean containsValue(Object value) | boolean | 是否包含value(遍历,O(n)) | map.containsValue(1) |
HashSet vs HashMap 区别及 HashSet 使用指南
核心区别
| 特性 | HashSet | HashMap |
|---|---|---|
| 数据结构 | 只存储单个元素 | 存储键值对 |
| 实现方式 | 底层是 HashMap (元素作为 key) | 哈希表 + 链表/红黑树 |
| 用途 | 去重、判断元素是否存在 | 根据 key 查找 value |
| 元素重复 | 元素不能重复 | key 不能重复,value 可以重复 |
| 性能 | 添加/删除/查找: O(1) | 添加/删除/查找: O(1) |
| 内存占用 | 相对较小 | 相对较大(存 key 和 value) |
HashMap的实现:
本质上由数组组成,数组存储哈希桶(每个地址对应一个哈希桶),通过 (数组长度-1) & 哈希值 计算得到地址,每个地址下的哈希桶由链表或者红黑树组成
在java8之后:当哈希桶长度小于8时,用链表实现;当哈希桶长度大于等于8且小于等于64时会退化为红黑树(树的查询复杂度为logn)
HashSet的实现:
内部依赖HashMap
| HashSet 方法 | 实际调用 HashMap | 返回值处理 |
|---|---|---|
add(E e) | map.put(e, PRESENT) | put 返回 null 表示插入成功,返回旧值表示已存在 |
remove(Object o) | map.remove(o) | 返回 PRESENT 表示删除成功,null 表示不存在 |
contains(Object o) | map.containsKey(o) | 直接返回 |
size() | map.size() | 直接返回 |
iterator() | map.keySet().iterator() | 遍历 KeySet |
clear() | map.clear() | 直接委托# 力扣热题100-《哈希》 |