【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version

这篇具有很好参考价值的文章主要介绍了【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题库链接:https://leetcode.cn/problem-list/e8X3pBZi/
【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

类型 题目 解决方案
哈希表的设计 剑指 Offer II 030. 插入、删除和随机访问都是O(1) 的容器 HashMap + ArrayList ⭐
剑指 Offer II 031. LRU 缓存 HashMap + 双向链表 ⭐
哈希表的应用 剑指 Offer II 032. 有效的变位词 哈希表:数组模拟哈希表 ⭐
剑指 Offer II 033. 变位词组 排序:Arrays.sort(arr) ⭐
剑指 Offer II 034. 外星语言是否排序 哈希表:数组模拟哈希表 ⭐
剑指 Offer II 035. 最小时间差 排序:计算两两差值 + 鸽巢原理 ⭐

本章主要练习了基本的哈希表设计和应用:

  • 哈希表的时间效率很高,添加、删除和查找操作的时间复杂度都是 O(1)
  • 哈希表经常被用来记录字符串中字母出现的次数字符串中字符出现的位置等信息;
  • 如果哈希表的键的数目是固定的,并且数目不太大,那么也可以使用数组来模拟哈希表,数组的下标对应哈希表的键,而数组的值则与哈希表的值对应。(与哈希表相比,数组的代码更加简洁,应聘者在面试时只要情况允许就可以尽量使用数组模拟哈希表

1. 剑指 Offer II 030. 插入、删除和随机访问都是O(1) 的容器 – P76

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构:

  • insert(val):当元素 val 不存在时返回 true ,并向集合中插入该项,否则返回 false 。
  • remove(val):当元素 val 存在时返回 true ,并从集合中移除该项,否则返回 false 。
  • getRandom:随机返回现有集合中的一项。每个元素应该有 相同的概率 被返回。

1.1 HashMap + ArrayList – O(1)(⭐)

时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( n ) O(n) O(n)

🎈 注意:该题比较麻烦的地方在于删除一个元素,本题解采用的方法是通过交换删除元素与变长列表中最后一个元素的位置,删除最后一个元素实现的,使用这种方法可以避免移动被删除元素后面的元素。

class RandomizedSet {
    Map<Integer,Integer> map;
    List<Integer> list;
    Random random;
    /** Initialize your data structure here. */
    public RandomizedSet() {
        map = new HashMap<>();
        list = new ArrayList<>();
        random = new Random();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if (map.containsKey(val)) return false;  // val存在 返回false
        int index = map.size();
        map.put(val,index);
        list.add(val);
        return true;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
        if (!map.containsKey(val)) return false;  // val不存在 返回false

        int index = map.get(val);  // val 在变长数组中的索引位置
        int last = list.get(list.size()-1);  // 变长数组中最后位置的元素
        list.set(index, last);  // 交换元素
        map.put(last, index);
        list.remove(list.size()-1);  // 删除元素
        map.remove(val);
        return true;

    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        int randomIndex = random.nextInt(list.size());
        return list.get(randomIndex);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

2. 剑指 Offer II 031. LRU 缓存 – P79

运用所掌握的数据结构,设计和实现一个 LRU (Least Recently Used,最近最少使用) 缓存机制 。
实现 LRUCache 类

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存;
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 ;
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

2.1 HashMap + 双向链表 – O(1)(⭐)

时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( n ) O(n) O(n)

🎈 提示:此处双向链表使用头插法或者尾插法均可,尾插法代码可参考:

  • LCR 031. LRU 缓存 - 力扣(LeetCode)
class LRUCache {
   // 1. 头插法,从头节点插入,从尾节点删除
    class DuLNode {  // 双向链表
        DuLNode prev;
        DuLNode next;
        int key;
        int value;
        public DuLNode(){};
        public DuLNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    DuLNode head;  // 头节点
    DuLNode tail;  // 尾节点
    int size;  // 容量
    Map<Integer,DuLNode> map;

    public LRUCache(int capacity) {
        size = capacity;
        map = new HashMap<>();
        head = new DuLNode();
        tail = new DuLNode();
        head.next = tail;  // head 和 tail 是两个哨兵节点
        tail.prev = head;
    }

    public void deleteNode(DuLNode node) {  // 删除链表中的某节点
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    public void addToHead(DuLNode node) {  // 链表头插法
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
        node.prev = head;
    }

    public void moveToHead(DuLNode node) {  // 将某节点移动到头
        deleteNode(node);
        addToHead(node);
    }
    
    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        DuLNode node = map.get(key);
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            DuLNode node = map.get(key);
            node.value = value;
            moveToHead(node);
        } else {
            DuLNode node = new DuLNode(key, value);
            if (map.size() == size) {
                DuLNode delete = tail.prev;
                deleteNode(delete);
                map.remove(delete.key);
            }
            map.put(key, node);
            addToHead(node);
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

PS:补充知识1 - 【双向链表】

🎈 可参考
[1] 数据结构笔记(六)-- 双向链表 - 淡定的炮仗的博客【图解鲜明】
[2] 线性表的链式表示-双向链表 - yunfan188的博客【代码示例】

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

// 线性表的双向链表存储结构
class DuLNode {  // 双向链表
    DuLNode prev;
    DuLNode next;
    int key;
    int value;
    public DuLNode(){};
    public DuLNode(int key, int value) {
        this.key = key;
        this.value = value;
    }
}

3. 剑指 Offer II 032. 有效的变位词 – P83

给定两个字符串 s 和 t ,编写一个函数来判断它们是不是一组变位词(字母异位词)。
……
注意: s 和 t 中每个字符出现的次数都相同且字符顺序不完全相同,才能称 s 和 t 互为变位词。

3.1 哈希表:数组模拟哈希表 – O(n)(⭐)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 26 ) O(26) O(26)

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length() || s.equals(t)) return false;  // 长度不一致/两者一样

        int[] map = new int[26];

        for (char c : s.toCharArray()) {  // 遍历 s,存入map
            map[c-'a']++;
        }

        for (char c : t.toCharArray()) {  // 遍历 t,存入map,并判断是否有字符次数不同的情况
            map[c-'a']--;
            if (map[c-'a'] < 0) return false;
        }
        return true;
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?将 int[26] 数组替换成无固定大小的 HashMap 即可,但与 HashMap 相比,使用数组会更快一些,因此只要情况允许就应该尽量使用数组模拟哈希表。

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length() || s.equals(t)) return false;  // 长度不一致/两者一样

        Map<Character, Integer> map = new HashMap<>();

        for (char c : s.toCharArray()) {  // 遍历 s,存入map
            map.put(c, map.getOrDefault(c,0)+1);
        }

        for (char c : t.toCharArray()) {  // 遍历 t,存入map,并判断是否有字符次数不同的情况
            map.put(c, map.getOrDefault(c,0)-1);;
            if (map.get(c) < 0) return false;
        }
        return true;
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

3.2 排序:Arrays.sort(arr) – O(nlogn)

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( l o g n ) O(logn) O(logn)

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length() || s.equals(t)) {
            return false;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

4. 剑指 Offer II 033. 变位词组 – P85

给定一个字符串数组 strs ,将 变位词 组合在一起。 可以按任意顺序返回结果列表。

4.1 映射:字符 - 质数 – O(mn)

时间复杂度 O ( m n ) O(mn) O(mn),空间复杂度 O ( n ) O(n) O(n)

🎈 说明:该解法是将 26 个小写字母均映射为了一个质数,这样的话,如果是变位词,那么一定具有相同的值。但该解法存在一个潜在的问题:即由于把字符映射到数字用到了乘法,因此当单词非常长时,乘法就有可能溢出。

class Solution {
    // 1. 字符映射
    public List<List<String>> groupAnagrams(String[] strs) {
        int[] hash = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101};

        Map<Long, List<String>> map = new HashMap<>();

        for (String str : strs) {
            char[] ch = str.toCharArray();
            long key = 1;
            for (char c : ch) {
                key *= hash[c-'a']; 
            }
            map.putIfAbsent(key, new ArrayList<String>());  // key不存在,才加入
            map.get(key).add(str);
        }

        return new ArrayList<>(map.values());
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

PS:补充知识1 - 【求质数】

🎈 可参考
[1] Java求质数常见的3种方式 - 十三度灰的博客

以埃氏筛法(埃拉托斯特尼筛法)为例:【模板题】AcWing 868. 筛质数

💡 原理:先把从2开始的所有数写下来,然后从2开始,将每个质数的倍数都标记成合数,即非质数,直到筛完所有小于等于给定数n的数。这样,留下的就是小于等于n的质数。

import java.util.*;

class Main {
    // 1. 埃氏筛法
    private final static int N = 1000010;
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        List<Integer> primes = new ArrayList<>();
        boolean[] isPrime = new boolean[N];
        
        for (int i = 2; i <= n; i++) {
            if (!isPrime[i]) {  // 是质数
                primes.add(i);
                if (primes.size() == 26) break;
                for (int j = i+i; j <= n; j += i) {
                    isPrime[j] = true;
                }
            } 
        }
        System.out.println(primes);
        sc.close();
    }
}

4.2 排序:Arrays.sort(arr) – O(nmlogm)(⭐)

时间复杂度 O ( n m l o g m ) O(nmlogm) O(nmlogm),空间复杂度 O ( n m ) O(nm) O(nm)

🎈 说明:对变位词进行排序,同一类变位词排序后会得到相同的字符串。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            char[] array = str.toCharArray();
            Arrays.sort(array);
            String key = new String(array);
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

4.3 计数:StringBuilder – O(nm)

时间复杂度 O ( n m ) O(nm) O(nm),空间复杂度 O ( n m ) O(nm) O(nm)

🎈 说明:记录变位词每个字符出现的次数,并将其形成字符串(eg:a2b3c1),同样同一类变位词形成的字符串一定相同。

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 length = str.length();
            for (int i = 0; i < length; i++) {
                counts[str.charAt(i) - 'a']++;
            }
            // 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 26; i++) {
                if (counts[i] != 0) {
                    sb.append((char) ('a' + i));
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

5. 剑指 Offer II 034. 外星语言是否排序 – P87

给定一组用外星语书写的单词 words,以及其字母表的顺序 order,只有当给定的单词在这种外星语中按字典序排列时,返回 true;否则,返回 false。

5.1 哈希表:数组模拟哈希表 – O(nm)(⭐)

时间复杂度 O ( n m ) O(nm) O(nm),空间复杂度 O ( 26 ) O(26) O(26)

class Solution {
    // 1. 数组模拟哈希表
    public boolean isAlienSorted(String[] words, String order) {
        int[] map = new int[26];

        for (int i = 0; i < order.length(); i++) {
            map[order.charAt(i)-'a'] = i;  // 构建哈希表,key为英文字符,value为字典顺序
        }

        for (int i = 0; i < words.length-1; i++) {
            if (!isOrder(words[i], words[i+1], map)) {
                return false;
            }
        }
        return true;
    }

    public boolean isOrder(String w1, String w2, int[] map) {
        int i = 0;
        for (; i < w1.length() && i < w2.length(); i++) {
            char c1 = w1.charAt(i);
            char c2 = w2.charAt(i);
            if (map[c1-'a'] < map[c2-'a']) return true;  // 正确排序
            if (map[c1-'a'] > map[c2-'a']) return false;  // 错误排序
        }
        return i == w1.length();
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java

6. 剑指 Offer II 035. 最小时间差 – P88

给定一个 24 小时制(小时:分钟 “HH:MM”)的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。

6.1 排序:计算两两差值 + 鸽巢原理 – O(nlogn)(⭐)

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    public int findMinDifference(List<String> timePoints) {
        int n = timePoints.size();
        if (n > 1440) {  // 鸽巢原理
            return 0;
        }
        int res = Integer.MAX_VALUE;
        Collections.sort(timePoints);
        int fp = getMinite(timePoints.get(0));  // 记录第一个元素
        int prev = fp;  // 通过 prev 记录前一元素
        for (int i = 1; i < timePoints.size(); i++) {  // 开始两两比较,记录最小值
            int cur = getMinite(timePoints.get(i));
            res = Math.min(res, cur - prev);
            prev = cur;
        }
        res = Math.min(res, fp + 1440 - prev);  // 处理首尾 00:00
        return res;
    }

    public int getMinite(String t) {  // 获取时间转换的分钟值
        return ((t.charAt(0) - '0') * 10 + t.charAt(1) - '0') * 60 + (t.charAt(3) - '0') * 10 + t.charAt(4) - '0';
    }
}

【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version,# 剑指 Offer(专项突破版),算法,八股,Java文章来源地址https://www.toymoban.com/news/detail-687629.html

到了这里,关于【LeetCode】剑指 Offer Ⅱ 第5章:哈希表(6道题) -- Java Version的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【剑指offer专项突破版】整数篇(经典面试题)——C

    剑指offer专项突破版(力扣官网) —— 点击进入 本文所属专栏 ——点击进入 总结题目要求: 1.目的—— 实现除法 2.要求—— 不得使用 * / % 3.处理溢出—— -2 32 / - 1 返回2 31 - 1 4. 整数除法—— 向0取整 , 说明:在其他语言中,比如 Python的除法是相负无穷取整 的—— -7 /

    2024年02月07日
    浏览(33)
  • 《剑指 Offer》专项突破版 - 面试题 47 : 二叉树剪枝(C++ 实现)

    题目链接 :LCR 047. 二叉树剪枝 - 力扣(LeetCode) 题目 : 一棵二叉树的所有节点的值要么是 0 要么是 1,请剪除该二叉树中 所有节点的值全都是 0 的子树 。例如,在剪除下图 (a) 中二叉树中所有节点值都为 0 的子树之后的结果如下图 (b) 所示。 分析 : 首先分析哪些子树会被

    2024年02月20日
    浏览(29)
  • 《剑指 Offer》专项突破版 - 面试题 4 : 只出现一次的数字(C++ 实现)

    题目链接 :137. 只出现一次的数字 II - 力扣(LeetCode) 题目 : 输入一个整数数组,数组中只有一个数字出现了一次,而其他数字都出现了 3 次。请找出那个只出现一次的数字。例如,如果输入的数组为 [0, 1, 0, 1, 0, 1, 100],则只出现一次的数字是 100。 分析 : 这个题目有一个

    2024年02月02日
    浏览(35)
  • 《剑指 Offer》专项突破版 - 面试题 15 : 字符串中的所有变位词(C++ 实现)

    题目链接 :LCR 015. 找到字符串中所有字母异位词 - 力扣(LeetCode) 题目 : 输入字符串 s1 和 s2,如何找出字符串 s2 的所有变位词在字符串 s1 中的起始下标?假设两个字符串中只包含英文小写字母。例如,字符串 s1 为 \\\"cbadabacg\\\",字符串 s2 为 \\\"abc\\\",字符串 s2 的两个变位词 \\\"c

    2024年01月18日
    浏览(42)
  • 《剑指 Offer》专项突破版 - 面试题 13 : 二维子矩阵的数字之和(C++ 实现)- 二维前缀和

    题目链接 :LCR 013. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode) 题目 : 输入一个二维矩阵,如何计算给定左上角坐标和右下角坐标的子矩阵的数字之和?对于同一个二维矩阵,计算子矩阵的数字之和的函数可能由于输入不同的坐标而反复调用多次。 例如,对于下图中的二

    2024年01月18日
    浏览(29)
  • 【LeetCode】剑指 Offer 64. 求1+2+…+n p307 -- Java Version

    题目链接 :https://leetcode.cn/problems/qiu-12n-lcof/ 求 1+2+...+n ,要求不能使用 乘除法 、 for 、 while 、 if 、 else 、 switch 、 case 等及 条件判断语句 (A?B:C)。 【测试用例】: 【条件约束】: 时间复杂度 O(n),空间复杂度 O(n) 【 解题思路 】: 关于求 1+ 2 + 3 + … + n的解法有很

    2023年04月22日
    浏览(33)
  • 【LeetCode】剑指 Offer 41. 数据流中的中位数 p214 -- Java Version

    题目链接 :https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

    2023年04月08日
    浏览(45)
  • 【leetcode】 剑指 Offer学习计划(java版本含注释)(下)

    该链接的学习计划如下: 剑指 Offer学习计划 上一版本的博文链接如下: 【leetcode】 剑指 Offer学习计划(java版本含注释)(上) 此贴两年前写的,一直在草稿箱里,今天释放出来,后续有时间完善下!(= - =) 题目: 输入一个非负整数数组,把数组里所有数字拼接起来排成

    2024年03月08日
    浏览(35)
  • 《剑指Offer》笔记&题解&思路&技巧&优化 Java版本——新版leetcode_Part_3

    当你踏入计算机科学的大门,或许会感到一片新奇而陌生的领域,尤其是对于那些非科班出身的学子而言。作为一位非科班研二学生,我深知学习的道路可能会充满挑战,让我们愿意迎接这段充满可能性的旅程。 最近,我开始了学习 《剑指Offer》 和Java编程的探索之旅。这不

    2024年02月22日
    浏览(36)
  • 力扣256.翻转二叉树(递归/qBFS) 剑指offer 32 从上到下打印二叉树(q BFS)I II III(三道题)

    采用队列 采用递归 第一个需要考虑的问题是 二维数组怎样在不知道行和列的情况下进行插入 :先定义一维数组,然后将一维数组插入二维数组! 第二个需要考虑的问题是 BFS中队列进行遍历每一层的时候既需要把当前结点的左右孩子结点存到队列里,同时当前层结束后 原来

    2024年02月16日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包