Leetcode记录库数据结构篇之四:哈希表

本文概述了哈希表在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;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 拓展延伸

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;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 拓展延伸

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;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 拓展延伸

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;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 拓展延伸

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;
    }
}

注意事项

  1. 注意事项

拓展延伸

  1. 遍历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);
        });
    }
}
  1. 获取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; // 没有重复元素
    }
}

注意事项

  1. 注意事项

拓展延伸

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值