哈希表题目解析:什么时候用数组做Hash表?什么时候用Set?

什么时候用哈希表?

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。  这句话很重要,大家在做哈希表题目都要思考这句话。 

哈希表基础知识

在这里先讲下哈希表基础知识:我们都知道数组可以通过下标索引直接访问,比如array[0],时间复杂度是O(1),那么哈希表也是通过key-value对应的直接查询的表,本质数组也是哈希表的一种。

映射

举例:如何快速判断一个学生是否是  学算法大学  的呢?也就是key="张三同学",若value=“学算法大学”   就对了。那么就生成一个Hashtable,把value=“学算法大学“的学生名字映射为0.1...n这样的下标,这样就可以用学生的名字当作数组的下标,快速查下表对应的value值了。

怎么名字映射为数字下标呢?哈希函数:哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

哈希碰撞

但是如果两个名字都映射到一个索引呢?这就引入新问题:哈希碰撞

解决方式有两种:线性探测和拉链法:根据TableSize也就是哈希表索引长度是否能足够包含所有的名字映射(学生总数)。

使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。

例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:

刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了

其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

例题

例题1:用数组做Hash题

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

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1: 输入: s = "anagram", t = "nagaram" 输出: true

示例 2: 输入: s = "rat", t = "car" 输出: false

说明: 你可以假设字符串只包含小写字母。

思路:采用的不是hash,但是却是hash映射的思路,我们把字母a-z映射为一个a[0..25]的数组,把输入的字符串都减去字节'a',获得的值就是从0-25的下标;比如输入的“asd”就存入数组中各个位置,再用输入的字符串t,剪一下就知道是否完全匹配,

代码细节:从字符串中取每个字符用s.charAt();数组int[26]才对,长度是26,下标是0-25

class Solution {
    public boolean isAnagram(String s, String t) {

        int[] a = new int[26];

        for(int i = 0;i<s.length();i++){
            a[s.charAt(i)-'a']++;
        }

        for(int i = 0;i<t.length();i++){
            a[t.charAt(i)-'a']--;
        }
        for(int count:a){
            if(count!=0)
            {
                return false;
            }
        }
        return true;

    }
}

遇到哈希问题我直接都用set不就得了,用什么数组啊。

直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。

不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。

例题二:用set

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2

这道题目没有限制数值的大小,就无法使用数组来做哈希表了。

而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

哈希表擅长解决:给你个元素判断是否出现过。

数值很大数组没办法,用set

思路:

把nums1转化为哈希表,用nums2查,结果放到result中。

哈希表中存储的数据是通过hashcode计算后得到一个数字比如1179395,然后存储进去。再来数据比较还是通过hashcode计算后比较。而且不能存相同的数据,因为计算出来的数字是一样的。

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> set2 = new HashSet<>();

        for(int i:nums1){
            set1.add(i);
        }

        for(int i:nums2){
            if(set1.contains(i)){
                set2.add(i);
            }
        }
        int[] ans = new int[set2.size()];
        int j=0;
        for(int i:set2){
            ans[j++]=i;
        }
        return ans;


    }
}

例题三:循环有重复的时候跳出使用hashset.contains

202. 快乐数 - 力扣(LeetCode)

思路.对n逐位平方和形成新数字存入hash中,判断不是1且未出现过,若是就继续循环平方和。

 

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        while(n!=1&&!set.contains(n)){
            set.add(n);
            n = kuaileshu(n);
        }
        if(n == 1){
            return true;
        }else{
            return false;
        }
        

    }
    private int kuaileshu(int n){
        int temp=0;
        while(n>0){
            temp += (n%10)*(n%10);
            n=n/10;
        }
        return temp;
    }
}

例题4:用HashMap做题Key-value

  • set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
  • 本题map是用来存什么的?是用来存放遍历过的元素和下标,target-cur然后查找出现过?

 代码:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        if(nums.length==0||nums==null){
            return res;
        }
        Map<Integer,Integer> map = new HashMap<>();
        int i=0;
        for(;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                res[0] = i;
                res[1] = map.get(target-nums[i]);
                break;
            }else{
                map.put(nums[i],i);
            }
        }
        return res;


    }
}

  • 29
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值