Leetcode记录库数据结构篇之四:哈希表
结论
一些很主观的东西,归根到底可能还是自己的实力不够:
哈希表和集合的用法很像,都可以用来去重。而且他们的操作时间复杂度都是O(1)的。
哈希表经常用在动态规划的思想里做dp记忆。
1 1. 两数之和
思路描述
- 暴力O(n2)
代码实现
java代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
int ft = 0, snd = 0, n = nums.length;
for(; ft < n; ft++){
for(snd = ft + 1; snd < n; snd++){
if(nums[ft] + nums[snd] == target){
return new int[]{ft, snd};
}
}
}
return new int[]{ft, snd};
}
}
思路描述
- 现将数组转成哈希表,在转换的过程中判断是否有 key = target - nums[i] 已经存在了,如果存在则找到了一对数字满足要求。
代码实现
java代码:
class Solution {
public boolean containsDuplicate(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int n = nums.length;
for(int i = 0; i < n; i++){
if(map.containsKey(nums[i])){
return true;
} else {
map.put(nums[i], 1);
}
}
return false;
}
}
注意事项
- 注意事项
拓展延伸
- 拓展延伸
2 217. 存在重复元素
思路描述
- 现将数组转成哈希表,在转换的过程中判断是否有 key = nums[i] 已经存在了,如果存在则存在重复数组。
- 时间复杂度O(n), 空间O(n)
代码实现
java代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
int n = nums.length;
for(int i = 0; i < n; i++){
if(map.containsKey(target - nums[i]) == true){
return new int[]{map.get(target - nums[i]), i};
} else {
map.put(nums[i], i);
}
}
return new int[]{0 , 0};
}
}
思路描述
- 先将数组转成集合,在最后判断集合的 size 与 nums 的length的关系。
- 集合会自动去重,时间复杂度O(n), 空间O(n)
代码实现
java代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
Set<Integer> set = new HashSet<>();
for (int num : nums) {
set.add(num);
}
return set.size() < nums.length;
}
}
注意事项
- 注意事项
拓展延伸
- 拓展延伸
3 594. 最长和谐子序列
思路描述
- 题目中没有要求保留原始顺序,所以我们现将 nums 转成 map(nums[i], 元素出现次数),然后对每一个 nums[i] 求出 nums[i] + 1 的出现的次数 + nums[i] 出现的次数,找到这个最大值即为所求。
代码实现
java代码:
class Solution {
public int findLHS(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int longest = 0;
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
for(int num : nums){
if(map.containsKey(num + 1)){
longest = Math.max(longest, map.get(num) + map.get(num + 1));
}
}
return longest;
}
}
注意事项
- 注意事项
拓展延伸
- 拓展延伸
4 128. 最长连续序列
思路描述
- 这道题用到了动态规划
- 首先写的是递归的方式
代码实现
java代码:
class Solution {
HashMap<Integer, Integer> map = new HashMap<>(); // dp
public int longestConsecutive(int[] nums) {
int maxLen = 0; // 默认最小长度
for(int num : nums){
map.put(num, 1); // 转为map
}
for(int num : nums){
int cnt = findLess(num - 1) + 1; // 递归找到每一个数字的前导连续递增数字的个数
map.put(num, cnt); // 记录
maxLen = Math.max(maxLen, cnt); // 更新最大值
}
return maxLen;
}
private int findLess(int num){ // 递归逻辑
if(!map.containsKey(num)){ // 如果当前map中没有此数字,返回0
return 0;
}
if(map.get(num) > 1){ // 如果num出现的次数大于1,说明这个数字的前导串长度已经找过了,直接返回
return map.get(num);
}
int numLen = 1 + findLess(num - 1); // 否则递归寻找前导串长度
map.put(num, numLen); // 更新
return numLen;
}
}
思路描述
- 迭代实现
代码实现
java代码:
class Solution {
HashSet<Integer> set = new HashSet<>(); // dp
public int longestConsecutive(int[] nums) {
int maxLen = 0; // 默认最小长度
for(int num : nums){
set.add(num); // 转为set
}
for(int num : nums){
if(!set.contains(num + 1)){ // 如果存在num + 1,说明不用从num开始计算,因为num + 1的长度一定更长
int cntNum = num;
int len = 1;
while(set.contains(--cntNum)){ // 如果存在--cntNum
len++;
}
maxLen = Math.max(maxLen, len);
}
}
return maxLen;
}
}
注意事项
- 注意事项
拓展延伸
- 拓展延伸
5 1436. 旅行终点站
思路描述
- 使用map,首先遍历给出的 List ,将其中给出的路径对转成 map 中的 <K, V> 数据对
- 然后,在遍历 map ,查看哪个 V 再做 K 时没有与之对应的 V , 说明 V 是终点。
代码实现
java代码:
class Solution {
public String destCity(List<List<String>> paths) {
HashMap<String, String> map = new HashMap<>();
for (List<String> path : paths) {
map.put(path.get(0), path.get(1));
}
String city = paths.get(0).get(0); // 获取 List 中的第一个城市作为起点
while(map.containsKey(city)){
city = map.get(city);
}
return city;
}
}
注意事项
- 注意事项
拓展延伸
- 遍历List的四种方法:
public class Test {
public static void main(String[] args) {
// 循环遍历List的4中方法
List<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
strList.add("ccc");
// 1. 普通遍历方式
for (int i = 0; i < strList.size(); i++) {
System.out.println(strList.get(i));
}
// 2.增强的for循环
for (String str : strList) {
System.out.println(str);
}
// 3. 使用Iterator迭代器
Iterator<String> it = strList.iterator();
while (it.hasNext()) {
String str = (String) it.next();
System.out.println(str);
}
// 4. java8 Lambda方式
// strList.forEach(System.out::println);//和下面的写法等价
strList.forEach(str -> {
System.out.println(str);
});
}
}
- 获取List中指定元素的方法。
public static void main(String[] args){
List<String>list = new ArrayList<String>();
list.add("保护环境"); //向列表中添加数据
list.add("爱护地球"); //向列表中添加数据
list.add("从我做起"); //向列表中添加数据
String ret = list.get(1);
System.out.println("获取索引位置为1的元素:"+ret);
}
运行结果如下:
获取索引位置为1的元素:爱护地球
6 剑指 Offer 03. 数组中重复的数字
思路描述
- 最基本的想法:使用哈希表进行计数,判断某个数字出现的次数。
- 但是这样的解法,不管是使用计数法还是新建HashMap都会有较高的空间复杂度。
- 时空复杂度O(n) O(n)
代码实现
java代码:
class Solution {
public int findRepeatNumber(int[] nums) {
// 计数法
int[] count = new int[100001];
Arrays.fill(count, 0);
int n = nums.length;
int tmp;
for (int i = 0; i < n; i++) {
tmp = ++count[nums[i]];
if (tmp == 2) {
return nums[i];
}
}
return -1;
}
}
思路描述
- 进阶方法是:以索引值作为数组元素的哈希位置,判断是否发生了哈希冲突。
- 时空复杂度O(n) O(1)
代码实现
java代码:
class Solution {
public int findRepeatNumber(int[] nums) {
int n = nums.length;
int i = 0, tmp;
while (i < n) {
if (nums[i] == i) { // 如果元素值等于它所处的索引位置,说明这个元素位置是正确的
i++; // 向后遍历
continue;
}
// 如果元素值不等于它所处的索引位置,让其归位
tmp = nums[i]; // 暂存
if (nums[tmp] == tmp) { // 如果我们发现当前元素值对应的索引位置上以及有了相同的元素,说明找到了重复元素
return tmp; // 返回
}
nums[i] = nums[tmp]; // 否则进行位置交换,将当前元素放到正确的位置上
nums[tmp] = tmp;
}
return -1; // 没有重复元素
}
}
注意事项
- 注意事项
拓展延伸
- 遍
本文概述了哈希表在LeetCode中的四个经典问题(两数之和、存在重复元素、最长和谐子序列、最长连续序列)中的关键策略,包括哈希表去重、动态规划和哈希冲突解决。通过实例解析和代码实现,展示了哈希表在优化算法效率中的重要性。

被折叠的 条评论
为什么被折叠?



