代码随想录算法训练营第5天 | 哈希表: 242.有效的字母异位词、 349. 两个数组的交集、 202. 快乐数 、1. 两数之和

一、哈希表理论基础

哈希表(Hash table)是根据键(Key)而直接访问内存存储位置的数据结构。它通过一个哈希函数(Hash Function)将键映射到一个存储桶(Bucket)或槽(Slot)中。哈希函数的作用是把任意长度的输入(键)通过计算转换为固定长度的输出,这个输出值就是哈希值,哈希值对应着哈希表中的一个位置。

理想情况:

在理想情况下,哈希函数能够将不同的键均匀地映射到哈希表的各个位置,即不会出现多个键映射到同一个位置的情况(这种情况称为哈希冲突)。此时,无论哈希表中有多少个元素,我们只需要通过哈希函数计算一次哈希值,然后直接访问对应的位置,这个过程的时间复杂度是常数级别的,即 o(1)。

哈希冲突

在实际应用中,哈希冲突是不可避免的,即不同的键可能会映射到同一个哈希值。为了解决哈希冲突,通常采用链地址法(Separate Chaining)或开放地址法(Open Addressing)等方法。当发生哈希冲突时,查找元素的时间复杂度可能会增加。例如,使用链地址法时,每个存储桶可能会存储一个链表或其他数据结构,当多个键映射到同一个存储桶时,需要在链表中进行线性查找,此时最坏情况下的时间复杂度会退化为 o(n),其中 n 是链表的长度。但如果哈希函数设计得好,哈希冲突的概率会比较小,平均情况下的时间复杂度仍然接近 o(1) 。

HashMap

HashMap 是 Java 集合框架中的一个类,它实现了 Map 接口,用于存储键值对(Key-Value)。HashMap 不保证元素的顺序,特别是不保证顺序恒久不变,允许使用 null 键和 null 值,但键必须是唯一的。

数据结构:Java 中的 HashMap 主要基于哈希表实现,在 JDK 1.8 及以后的版本中,采用了数组 + 链表 + 红黑树的数据结构。

  • 数组:作为哈希表的基础结构,每个数组元素称为一个桶(Bucket),用于存储键值对节点。
  • 链表:当发生哈希冲突(不同的键计算出相同的哈希值)时,新的键值对会以链表的形式存储在对应的桶中。
  • 红黑树:当链表的长度超过一定阈值(默认为 8),并且数组长度大于 64 时,链表会转换为红黑树,以提高查找、插入和删除操作的性能。

TreeMap

数据结构:TreeMap 基于红黑树(Red - Black Tree)这种自平衡的二叉搜索树实现。红黑树是一种特殊的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色(红色或黑色),通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。这种特性使得 TreeMap 在插入、删除和查找操作上能保证 O(log n) 的时间复杂度。

按键排序:TreeMap 会根据键的自然顺序或者创建 TreeMap 时提供的比较器(Comparator)来对键进行排序。如果键实现了 Comparable 接口,TreeMap 会使用其自然顺序;如果传入了自定义的 Comparator,则使用该比较器来决定键的顺序。

不允许键为 null:由于 TreeMap 要对键进行排序,而 null 无法参与比较,所以 TreeMap 不允许键为 null,但值可以为 null

二、242.有效的字母异位词

题目链接/文章讲解/视频讲解: 代码随想录

思路:创建26大小的数组,遍历字符串 s 的每一位的ASCII码。因为都是小写字母,所以是连续的,通过减去“a”的ASCII码,即可将a-z字符映射到0-25。遍历得到 s 中的各字母数量,再遍历 t 字符串,减去数组中的字符数量,判断最后数组是否均为 0,若为0则是字母异位词。

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']++;
      } 

      for(int j = 0; j<t.length(); j++){
        record[t.charAt(j) - 'a']--;
      } 

      for(int count:record){
        if(count != 0){
            return false;
        }
      }
      return true;
    }
}

时间复杂度 o(m+n)


三、349. 两个数组的交集

利用HashSet数据类型,Set代表无序不可重复集合,只能根据元素本身来访问。

将nums1的元素装入一个HashSet集合中,判断nums2中的元素是否存在(contains函数)于该数组中,并将重复的元素装进一个新的HashSet集合(不重复性),输出时转化为数组类型。

import java.util.HashSet;
import java.util.Set;

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if(nums1 == null || nums1.length == 0 || nums2==null || nums2.length==0){
            return new int[0];
        }
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> resSet = new HashSet<>();
        for(int i:nums1){
            set1.add(i);
        }
        for(int i:nums2){
            if(set1.contains(i)){
                resSet.add(i);
            }
        }
        int[] result=new int[resSet.size()];
        int j = 0;
        for(int i:resSet){
            result[j++]=i;
        }
        return result;
    }
}

四、202. 快乐数

题目链接/文章讲解:代码随想录

根据快乐数的定义,结果会得到1。若不是快乐数则会进入循环,那就会出现重复的数,还是用到哈希表用于快速查询一个元素是否出现在集合里。

记忆遍历整数每一位的方式。

class Solution {
    public boolean isHappy(int n) {
        //存储每次计算结果
        Set<Integer> sums = new HashSet<>();
        //没得到1就一直循环
        while( !sums.contains(1) ){
            int sum = 0;
           while(n>0){
                int a = n % 10;
                sum += a*a;
                n /= 10;
            }
            //出现重复则返回false
            if(sums.contains(sum)){
                return false;
            }
            sums.add(sum); 
            n = sum;
        }
        return true;
    }
}

五、1. 两数之和

题目链接/文章讲解/视频讲解:代码随想录

注意点:

  • 1、将数组装入Map集合时,应该以数值为key,索引为value,因为最后输出的是索引下标,要通过键来取值。
  • 2、要先判断后新增元素,防止一个元素重复使用。
  • 3、记忆在输出位置新增数组的格式。
class Solution {
    public int[] twoSum(int[] nums, int target) {
        if(nums == null || nums.length == 0){
        return null;
        }
        Map<Integer,Integer> numsMap = new HashMap<>();
        for(int i = 0 ; i<nums.length ; i++){
            int a = target - nums[i];
            if(numsMap.containsKey(a)){
                a = numsMap.get(a);
                return new int[] {a,i};
            }
            numsMap.put(nums[i],i);
        }
        return null;
    }        
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值