代码随想录第六天|哈希表part01--哈希表理论基础、242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和

资源引用:

哈希表理论基础(代码随想录 (programmercarl.com)

leetcode题目:

242.有效的字母异位词(242. 有效的字母异位词 - 力扣(LeetCode)

242.有效的字母异位词(242. 有效的字母异位词 - 力扣(LeetCode)

202.快乐数(202. 快乐数 - 力扣(LeetCode)

1.两数之和(1. 两数之和 - 力扣(LeetCode)

例行碎碎念:

今天不够自律,这两天起的都有些晚,休息的不是很好,希望能够尽快调整,另外由于准备考试的原因也没能及时完成day7的练习,希望自己能够在后面提高效率,慢慢补上,不过今天做题还是很有手感的,想法很对哈哈哈,总之加油!

哈希表理论基础(代码随想录 (programmercarl.com)

哈希表

定义:

哈希表(Hash Table),又名散列表

性质:

是根据关键码的值而直接进行访问的数据结构

示例:

数组就是一张哈希表,关键码是数组的索引下标,即可直接访问

应用:

快速判断一个元素是否存在于集合中

优势:

查询的时间复杂度为O(1)

引子:

将元素值映射到哈希表涉及到hash function——哈希函数

哈希函数

示例:

以“查询学校是否有该姓名的学生”为例,

哈希函数把学生的姓名直接映射为哈希表上的索引,如下图所示:

通过hashCode将名字转化为数值

hashCode通过特定编码方式,将其他数据格式转化为不同的数值,从而将学生名字(字符串)映射成哈希表上的索引数字(整型)

引子:

hashCode值>tableSize,对该值取模,防止越界。

该方式存在问题,当学生总数大于哈希表的大小,存在多个学生映射到相同索引下标的情况,通过哈希碰撞来处理。

哈希碰撞:

定义:

不同的两个数据映射到同一下标位置,叫做哈希碰撞

(1)拉链法:tableSize<dataSize

通过链表存储冲突元素,关键是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

(2)线性探测法(开放地址法):tableSize>dataSize

产生冲突,则向下遍历直至找到一个空位

常见的三种哈希结构:

1.数组
2.集合(set):数值不重复、不可更改,增删不冲突

TreeSet的底层实现是红黑树,这是一种平衡搜索二叉树,所以key值有序的且不可更改只能增删

HashSet的底层实现是哈希表,查询和增删效率是最优的,优先使用

集合

底层实现

是否有序

数值是否可以重复

能否更改数值

查询效率

增删效率

TreeSet

红黑树

有序

O(log n)

O(log n)

HashSet

哈希表

无序

O(1)

O(1)

3.映射(map):key值不重复、不可修改

(key, value)结构

TreeMap的底层实现是红黑树,这是一种平衡搜索二叉树,所以key值有序的且不可更改只能增删

HashMap的底层实现是哈希表,查询和增删效率是最优的,优先使用

映射

底层实现

是否有序

数值是否可以重复

能否更改数值

查询效率

增删效率

TreeMap

红黑树

key有序

key不可重复

key不可修改

O(logn)

O(logn)

HashMap

哈希表

key无序

key不可重复

key不可修改

O(1)

O(1)

总结:

快速判断一个元素是否存在于集合中——哈希法

牺牲空间换取时间——哈希法

242.有效的字母异位词(242. 有效的字母异位词 - 力扣(LeetCode)

题目分析:

字母异位词的意思是对原词中的全部字母重新排序,并使用且仅使用每个字母一次。需要快速查询每种字母是否对应存在且个数相同,考虑使用简单的哈希表——数组进行解决。

解题思路:

已知s、t仅存在小写字母,故构建长度为26的数组record[26]记录每个字母的出现频率,下标值通过s[i]-'a'的ASCII值对应,注意初始化(java自动实现)。

首先判断两个字符串长度是否相同或是否存在空字符串,如果不相同则直接返回false

其次先对其中一个字符串进行字母频率统计,出现则记录数组中对应key的value++

然后再对另一个字符串进行字母频率统计,出现则record中对应key的value--

最后遍历检查record中是否存在非零项,若存在非零项,说明不为字母异位词;若不存在,则表明是。

反思:

1.在java中,获取String s中的字符,通过s.charAt(i)的方式获取

2.在java中,获取数组的长度使用array.length,获取字符串的长度使用string.length()

3.在java中,获取任意一种哈希表的长度,推荐使用 .size()方法

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length() == 0 && t.length() == 0){
            return true;
        }
        if(s.length() != t.length() || s.length() == 0 || t.length() == 0){
            return false;
        }
        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 k=0;k<record.length;k++){
            if(record[k] != 0){
                return false;
            }
        }
        return true;
    }
}

349.两个数组的交集(349. 两个数组的交集 - 力扣(LeetCode)

题目分析:

求两个给定数组的交集,输出结果中的每个元素是唯一的,顺序无需考虑,考虑使用HashSet实现。

解题思路:

将nums1映射到hashSet,查询num2中的每个元素是否包含在hashSet中,如果有该元素存在,则在结果hashSet中添加该值。最后将hashSet转换成数组进行输出。

注意检查nums1和nums2是否为空,若为空则直接返回new int[0]

反思:

1.注意import java.util.*

2.for循环的遍历方式优化

//第一种遍历方式,j为指针
for(int j=0;j<nums2.length;j++){
    if(set1.contains(j)){
        resSet.add(j);
    }
}
//第二种遍历方式,j直接为数组元素
for (int j : nums2) {
    if (set1.contains(j)) {
        resSet.add(j);
    }
}

3.set和array的对比:set的占用空间大,速度慢,映射过程的hash计算需要耗时,在数据量大的情况,差距是很明显的。

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

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if(nums1.length == 0 || nums2.length == 0){
            return new int[0];
        }
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> resSet = new HashSet<>();
        for(int i=0;i<nums1.length;i++){
            set1.add(nums1[i]);
        }
        for(int j=0;j<nums2.length;j++){
            if(set1.contains(nums2[j])){
                resSet.add(nums2[j]);
            }
        }
        //方法1:将结果集合转为数组
        return resSet.stream().mapToInt(x -> x).toArray();
        
        //方法2:另外申请一个数组存放setRes中的元素,最后返回数组
        int[] arr = new int[resSet.size()];
        int j = 0;
        for(int i : resSet){
            arr[j++] = i;
        }
        return arr;
    }
}

202.快乐数(202. 快乐数 - 力扣(LeetCode)

题目分析:

快乐数n:该数满足经过有限次如下运算——每次运算将该数替换为它每一位数的平方和——最终结果为1。若不满足快乐数,则是无限循环但使用不为1,此处可以利用set判断其sum是否重复出现。

解题思路:

用一个set记录每一次重复该计算的得数sum,若第一次得出该数,则计入set,否则说明陷入循环,返回false。

若未陷入循环,则说明仍在有限次范围内,此时可以计算sum直至为1。(注意,值为1后也会陷入循环,必须注意判断)

关于取位方式,运用取余和除法。

总结:

  1. 返回值是boolean值,可以通过返回“判别式”来简化。
  2. 循环不变量条件要找准,①是否为1 ②是否陷入sum循环
  3. 注意第一个sum也要添入set
import java.util.*;
class Solution {
    public boolean isHappy(int n) {
        Set<Integer> sums = new HashSet<>();
        int sum=n;
        while(sum != 1 && !sums.contains(sum)){
            sums.add(sum);
            int res=sum,single=0,temp=0;
            do{
                single = res%10;
                temp += single*single;
                res = res/10;
            }while(res > 0);
            sum = temp;
        }
        return sum==1;
    }
}

1.两数之和(1. 两数之和 - 力扣(LeetCode)

题目分析:

给定一个整数数组nums和目标值target,请你在nums中找出和为target的两个整数,并返回数组下标——考虑使用HashMap,键为数组的值,值为数组的下标。

解题思路:

首先处理边界条件,如果nums为空则直接返回长度为2的空数组res

然后建立HashMap,键值对存储数组的值及其对应下标

接着遍历当前元素,在map中寻找是否存在匹配的key,如果存在,取其value和该元素下标共同存入res数组并结束遍历;若不存在,则将该元素及其下标作为新的键值对存入map

最后返回res数组

总结思考:

  1. 为什么使用哈希表:需要根据元素值来查找是否存在匹配的项
  2. 为什么用map:既要存储元素值用于查找匹配,又要最终返回其下标,故采用(key,value)
  3. 本题中map是用来存什么的:遍历数组中的元素,在map中查找是否有匹配的key,若不存在,将该元素及其下标以(key,value)的形式存入map,用于后续数组中的其他元素进行查找匹配
  4. map中的key和value存什么:分别存数组中的元素值和它的下标
  5. 逐个遍历并逐个加入map的优势:减少遍历次数和减少空间占用
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];   // 遍历当前元素,并在map中寻找是否有匹配的key
            if(map.containsKey(temp)){
                res[1] = i;
                res[0] = map.get(temp);
                break;
            }
            map.put(nums[i], i);    // 如果没找到匹配对,就把访问过的元素和下标加入到map中
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值