20230814 | HashTable Part1

定义部分来自<<代码随想录>>

Hash Table

Definition: 哈希表是根据关键码的值直接进行访问的数据结构,一般哈希表都是用来快速判断一个元素是否出现在集合里
eg.用名字查询是否在学校 -> Time Complexity: O(1)

如何将学生姓名映射到哈希表上 -> Hash Function

Hash Function

Definition: 通过hashCode把名字转化为数值, 一般hashcode是通过特定编码方式, 可以将其他数据格式转化为不同的数值
eg. 把学生名字映射为哈希表上的索引数字
Hash Function

如果hashCode得到的数值大于tableSize, 为了保证映射出来的索引数值都落在哈希表上 -> 对数值取模

如果学生数量大于tableSize -> 哈希碰撞

哈希碰撞

Definition: 映射到了同一索引下标

解决办法:

拉链法

发生冲突的元素都被存储在链表中
在这里插入图片描述
(数据规模是dataSize, 哈希表的大小为tableSize)
Notice: 选择适当的哈希表的大小 (数组空值 -> 浪费大量内存, 链表太长 -> 多余时间查找)

线性探测法

保证tableSize大于dataSize, 用哈希表中的空位来解决碰撞问题

常见的三种哈希结构

HashMap

HashMap是一种常用的哈希表实现,它使用键值对(Key-Value)的方式存储数据。它基于哈希函数将键映射到存储桶(buckets)上,这样可以快速地插入、删除和查找元素。HashMap允许空键和空值,但是不保证元素的顺序。

Time complexity: 平均查找、插入和删除操作 O(1)

import java.util.HashMap;

HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "One");
hashMap.put(2, "Two");
hashMap.put(3, "Three");

String value = hashMap.get(2); // 获取键为2的值,结果为 "Two"
TypeHashMapTreeMap
底层数据结构HashTable
O(1)
Red-Black Tree (一种自平衡二叉搜索树)
O(logn)
元素顺序无序 位置由哈希值决定有序性 元素按照键的自然顺序或指定的比较器顺序进行排序
键的要求key 必须是可哈希的 (有 hashCode() 和 equals() 方法)
允许键为 null
key 必须是可比较的 (实现 Comparable 接口或在构造时提供了比较器)
不允许 null 作为键
性能高效的插入、删除和查找操作 但不需要保持有序的情况需要有序遍历 (按照键的顺序) 以及范围查询的情况

HashSet

HashSet是基于HashMap实现的一种无重复元素的集合。它不允许重复的元素,并且不保证元素的顺序。HashSet是一种快速查找元素的数据结构,适用于需要存储唯一元素的情况。

Time complexity: 元素的存储和查找操作的时间复杂度是 O(1) 的平均情况

底层: HashSet 是通过构造一个 HashMap 来实现的,只不过在 HashSet 中,我们只关心键值(key)部分而忽略了值(value)部分。HashSet 中的元素实际上是作为 HashMap 中的 key 存储的。当我们调用 HashSet 的 add 方法时,实际上是将元素作为 HashMap 的 key 插入到 HashMap 中,而 value 部分使用一个常量对象。

import java.util.HashSet;

HashSet<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");

boolean containsBanana = hashSet.contains("Banana"); // 检查集合中是否包含 "Banana",结果为 true
TypeHashSetTreeSet
同 hashMap and TreeMap

Hashtable

Hashtable是一个早期的哈希表实现,它类似于HashMap,但是具有一些不同之处。Hashtable是线程安全的,但性能相对较差,而且不允许空键和空值。在多线程环境中,可以使用Hashtable来保证线程安全的操作。

import java.util.Hashtable;

Hashtable<String, Integer> hashtable = new Hashtable<>();
hashtable.put("One", 1);
hashtable.put("Two", 2);
hashtable.put("Three", 3);

int value = hashtable.get("Two"); // 获取键为 "Two" 的值,结果为 2

总结: 当我们要快速判断一个元素出现在集合里的时候, 就要考虑哈希法, 但其实是牺牲空间换取时间 -> 额外的set, map来存放数据

Leetcode242 Valid Anagram

题目链接: 有效的字母异位词

Method 1

两次 sort(s.begin(), s.end()) ascending order.

Method 2

两个字符串长度必须相同, 每个字符的数量也相同, 顺序可以不同. 可以用哈希表解决. 统计 s 字符串时, 各字符+1, 统计 t 字符串时, 各自符-1
Time Complexity: O(m+n)
Space Complexity: O(1)

class Solution {
    public boolean isAnagram(String s, String t) {
        HashMap<Character, Integer> result = new HashMap<>();
        for (char x: s.toCharArray()){
            result.put(x, result.getOrDefault(x,0) + 1);
        }
        for (char y: t.toCharArray()){
            result.put(y, result.getOrDefault(y, 0) - 1);
        }
        for (int num: result.values()){
            if (num != 0){
                return false;
            }
        }
        return true;
    }
}

Method 3

提前定义数组大小, 确定相对位置
Notice: s.charAt(i) 得到的是String的第一位字符, s.charAt(i) - 'a' 表示和 a 的相对index

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] record = new int[26];
        for (int i = 0; i < s.length(); i++){
            record[s.charAt(i) - 'a'] += 1;
        }
        for (int i = 0; i < t.length(); i++){
            record[t.charAt(i) - 'a'] -= 1;
        }
        for (int num: record){
            if (num != 0) return false;
        }
        return true;
    }
}

Leetcode349 Intersection of Two Arrays

题目链接: Intersection

[Example]:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]

一开始想仿照上题Method3, 但是发现无法确定数组的具体大小. 需要新建一个新的HashSet, 循环其中一个数组, 把里面的值加入set中

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if (nums1.length == 0 || nums2.length == 0) return new int[0];
        Set<Integer> set = new HashSet<>();
        Set<Integer> result = new HashSet<>();
        for (int num: nums1){
            set.add(num);
        }
        for (int num: nums2){
            if (set.contains(num)){
                result.add(num);
            }
        }
        return result.stream().mapToInt(x -> x).toArray();
    }
}

Leetcode202 Happy Number

题目链接

[Example]:
Input: n = 19
Output: true
Explanation:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

这道题的本质是看会不会出现重复和, 如果有和重复出现了, 说明会一直循环, 即永远得不到1. 创建一个set记录和
此外, digit sum的求法: digit / 10 digit % 10

class Solution {
    public boolean isHappy(int n) {
      Set<Integer> set = new HashSet<>();
        int temp = sum(n);
        while (temp != 1){
            if (set.contains(temp)){
                return false;
            }
            else{
                set.add(temp);
                temp = sum(temp);
            }
            
        }
        return true;
    }
    public int sum(int n){
        int sum = 0;
        while (n > 0){
            sum += (n % 10) * (n % 10);
            n  = n / 10;
        }
        return sum;  
    }
}

Leetcode1 Two sum

题目链接: 两数之和

[Example]:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Method1

最先想到的解法, 两个for loop循环直到得到需要的Target
Time Complexity: O(n^2)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        for (int i = 0; i < nums.length; i++){
            for (int j = i + 1; j < nums.length; j++){
                if (nums[i] + nums[j] == target){
                    result = new int[]{i,j};
                }
            }
        }
        return result;
    }
}

Method2

因为涉及到判断某元素是否出现在集合里, 考虑用HashSet, 又因为需要知道元素index, 需要用HashMap
查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法
Time Complexity: O(n)
Space Complexity: O(n)

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++){
        int temp = target - nums[i];   
        if(map.containsKey(temp)){
            res[1] = i;
            res[0] = map.get(temp);
            break;
        }
        map.put(nums[i], i);    
    }
    return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值