目录
理论基础
哈希表
用来快速判断一个元素是否出现集合里
核心优势在于高效的查找、插入和删除操作,使其非常适合需要频繁查找和修改数据的场景
数组
适用于题目限制数值的大小
eg题目给定一个包含 0 到 99 的整数数组,
set
适用于题目数值跨度大/分散
eg题目给定一个无序的整数数组
map
适用于要记录key, value
常见应用
包括快速查找和去重、频率统计、集合操作、缓存实现、索引构建等
键值映射
哈希表能够高效地将键映射到值,并根据键快速获取值,适合处理字典、缓存等问题。
- 字典(Dictionary):将键与值进行关联,存储并快速查找。例如,使用哈希表将用户名映射到用户信息。
- 缓存(Cache)实现:哈希表可用于实现LRU缓存,通过键值对来存储缓存数据,结合链表等数据结构维护缓存的访问顺序。
- 配置表存储:例如,存储应用程序的配置项,允许快速查询配置参数的值。
统计频率
哈希表可以用来统计数据出现的频率或次数,适合于计数问题。
- 统计单词或字符频率:例如,统计文章中每个单词的出现频率,或者统计一个字符串中每个字符的出现次数。
- 投票计数:统计选票中的每个候选人的票数。
查找数之和
给定一个数组,快速查找是否存在两个数的和为指定值,或寻找满足特定条件的数对。
- 两数之和问题:使用哈希表存储已经遍历过的数,然后在遍历的过程中查找目标值与当前元素的差值是否存在于哈希表中。
- 多数之和问题:例如,寻找数组中是否存在三数之和为某个指定值,也可以通过哈希表加速求解。
集合操作
- 集合的交集:通过哈希表快速查找两个集合的公共元素。
- 集合的并集:将多个集合合并,并保证结果集合中没有重复元素。
- 集合的差集:找到集合A中有但集合B中没有的元素。
字谜和变位词
- 变位词分组:将一组单词按照字母重排列后的形式分组。例如,将
"eat"
,"tea"
,"ate"
归为一组。 - 判断两个字符串是否是变位词:使用哈希表统计每个字符出现的频率,然后对比两个字符串的字符频率表。
大数据去重和查找
- 找出重复数据:在处理海量数据(如日志、网页请求等)时,哈希表可用于检测并移除重复数据。
- 流数据处理:对于动态输入的数据流,使用哈希表可以实时跟踪哪些元素已经出现过或频率最高的元素。
索引和倒排索引
- 数据库索引:哈希表能够根据关键字快速定位数据行,在数据库的查询优化中起到重要作用。
- 倒排索引:在搜索引擎中,倒排索引用于将文档中的关键字映射到文档ID,从而支持快速检索。
LRU缓存
在缓存系统中,哈希表与双向链表相结合,可以实现LRU缓存。
- 缓存淘汰机制:哈希表用于存储缓存中的键值对,链表则用于跟踪访问顺序,以便在缓存满时淘汰最不常用的项。
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) 的算法吗?
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++){//不能用foreach因为后面要用i
int temp=target-nums[i];
if(map.contaionsKey(temp)){
res[1]=i;
res[0]=map.get(temp);
break;//因为只要返回一组
}
map.put(nums[i],i); //没找到匹配对,就把访问过的元素和下标加入到map中
}
return res;//记得返回的是下标
}
}
49.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母
按照字母从小到大排序,可以得到同一个字符串,当且仅当两个字符串排序后一样,这两个字符串才能分到同一组
键为已排序,值的列表为未排序
举例:{"aet": ["eat", "tea", "ate"], "ant": ["tan", "nat"], "abt": ["bat"]}
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {//返回的结果belike[["bat"],["nat","tan"],["ate","eat","tea"]]
if (strs == null || strs.length == 0) return new ArrayList<>();
Map <String,List<String>> m=new HashMap<>();
for (String str:strs){
char[] s=str.toCharArray();
Arrays.sort(s);//传s!别弄混了
String newstr =new String(s);
if(!m.containsKey(newstr)) m.put(newstr,new ArrayList<>());
m.get(newstr).add(str);
}
return new ArrayList<>(m.values());
}
}
128.最长连续序列
给定一个未排序的整数数组 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
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
直接定位到每个连续序列的起点
起点:前一个数不存在于数组中
终点:后一个数不存在于数组中
class Solution {
public int longestConsecutive(int[] nums) {
int l,res=0;
Set <Integer> s= new HashSet<>();
for(int n:nums) s.add(n);
for(int n:nums){
if(!s.contains(n-1)){
l=1;
while(s.contains(++n)) l++; //当前数的下一个数所以++num
res=Math.max(l,res);
}
}
return res;
}
}
关于++num,等同于
num++;
while (s.contains(num)) {
l++;
num++;
}