算法训练营day06|哈希表|哈希表理论基础|242.有效的字母异位词,349.两个数组的交集,202.快乐数,1.两数之和

1.知识点

1.1哈希表

哈希表也叫散列表,是根据关键码值(key value)而直接访问元素的数据结构。他通过把关键码值映射到表中一个位置来访问记录,以加快访问速度.

给定表 M,存在函数 f(key) ,对任意给定的关键字值 key,放入函数后若可以得到包含改关键字的记录在表 M 中的位置,则称该表为哈希表,函数f为哈希函数

1.2哈希函数

将键映射到正确的位置的函数就是哈希函数,常用的哈希函数分为:

加法Hash,位运算Hash,乘法Hash,除法Hash,查表Hash等

参考:常用哈希函数介绍 - 知乎 (zhihu.com)

1.3哈希碰撞

哈希函数将两个不同的键映射到同一个索引的情况,就产生了哈希碰撞

1.4解决哈希碰撞

解决哈希碰撞的方法有

1.4.1开发地址法

哈希碰撞时,去寻找一个新的空闲的哈希地址

1.4.2再哈希法

同时构造多个不同的哈希函数,等发生冲突时就使用第2个,第3个...等其他的哈希函数计算地址,直到不发生冲突为止.虽然不易发生聚集,但是增加了计算时间.

1.4.3链地址法

将虽有哈希地址相同的记录记录在同一个链表中

1.4.4建立公共溢出区

将哈希表分为基本表和溢出表,将发生冲突的存在溢出表中

参考:解决哈希冲突(四种方法)_哈希冲突的解决方法_君诀的博客-CSDN博客

1.5常见哈希结构

数组,HashSet,HashMap

2.刷题

242.有效的字母异位词

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

题目描述

方法1:比较两个HashMap是否相等

package daimasuixiangshuati.day06_haxibiao;

import java.util.HashMap;

/**
 * @Author LeiGe
 * @Date 2023/10/10
 * @Description todo
 */
public class YouXiaoDeZiMuYiWeiCi242_2 {
    /**
     * 方法1-hashmap
     * 1.两个map分别计算出s和t两个字符串中每个字符出现的次数
     * 2.比较两个map的属性是否相等
     *
     * @param s
     * @param t
     * @return
     */
    public static boolean isAnagram(String s, String t) {
        HashMap<Character, Integer> sMap = wordFrequency(s);
        HashMap<Character, Integer> tMap = wordFrequency(t);
        return sMap.equals(tMap);
    }

    public static HashMap<Character, Integer> wordFrequency(String s) {
        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
        }
        return map;
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

方法2:数组作为哈希表

package daimasuixiangshuati.day06_haxibiao;

/**
 * @Author LeiGe
 * @Date 2023/10/10
 * @Description todo
 */
public class YouXiaoDeZiMuYiWeiCi242_3 {
    /**
     * 方法2-数组作为哈希表
     * 1.将第一个字符串的每个字符存放在数组中(下标表示字符,下标对应的值表示该字符出现的频率)
     * 2.遍历第二个字符串,每遇到一个字符,就将该字符对应的值减1
     * 3.再次遍历数组,如果数组中出现不为0的值,则返回false,否则返回true
     *
     * @param s
     * @param t
     * @return
     */
    public static boolean isAnagram(String s, String t) {
        int[] ints = new int[26];
        //1.将第一个字符串的每个字符存放在数组中(下标表示字符,下标对应的值表示该字符出现的频率)
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            //a对应数组0下标
            int idx = c - 'a';
            ints[idx] += 1;
        }
        //2.遍历第二个字符串,每遇到一个字符,就将该字符对应的值减1
        for (int i = 0; i < t.length(); i++) {
            char c = t.charAt(i);
            int idx = c - 'a';
            ints[idx] = ints[idx] - 1;
        }
        //3.再次遍历数组,如果数组中出现不为0的值,则返回false,否则返回true
        for (int anInt : ints) {
            if (anInt != 0) {
                return false;
            }
        }
        return true;
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

349.两个数组的交集

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

题目描述

方法1:HashSet

package daimasuixiangshuati.day06_haxibiao;

import java.util.HashSet;

/**
 * @Author LeiGe
 * @Date 2023/10/11
 * @Description todo
 */
public class LiangGeShuZuDeJiaoJi349_2 {

    /**
     * 方法1:hashSet
     * 1.将nums1中的数放入hashSet中
     * 2.遍历nums2,如果num2存在于hashSet中,将num2加入到结果中
     *
     * @param nums1
     * @param nums2
     * @return
     */
    public static int[] intersection(int[] nums1, int[] nums2) {
        if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
            return new int[0];
        }
        HashSet<Integer> set = new HashSet<>();
        HashSet<Integer> resSet = new HashSet<>();
        //遍历数组1
        for (int i : nums1) {
            set.add(i);
        }
        //遍历数组2的过程中判断哈希表中是否存在该元素
        for (int i : nums2) {
            if (set.contains(i)) {
                resSet.add(i);
            }
        }
        int[] resArr = new int[resSet.size()];
        int index = 0;
        //将结果转换为数组
        for (int i : resSet) {
            resArr[index++] = i;
        }
        return resArr;
    }
}

时间复杂度:O(N+M)

空间复杂度:O(N)

方法2:数组

package daimasuixiangshuati.day06_haxibiao;

import java.util.ArrayList;

/**
 * @Author LeiGe
 * @Date 2023/10/11
 * @Description todo
 */
public class LiangGeShuZuDeJiaoJi349_3 {
    /**
     * 方法2-散列表-数组
     * 0.因为数组最大元素是1000,用一个数组hash表示,hash 下标为元素,值为该元素出现的次数
     * 1.遍历nums1
     * 1.1如果nums1[i]在hash中对应的值为0,就+1
     * 1.2如果nums1[i]在hash中对应的值为1,则维持1,(达到去重效果)
     * 2.遍历数组nums2
     * 2.1如果nums2[i]在hash中对应的值为0,那么此元素不可能是公共值
     * 2.2如果nums2[i]在hash中对应的值为1,那么表示是公共元素,则+1
     * 2.3如果nums2[i]在hash中对应的值为2,那么表示是公共元素,维持2不变,(达到nums2的去重效果)
     * 3.此时hash中值为2的就是公共部分
     * 4.将ArrayList转化为数组
     *
     * @param nums1
     * @param nums2
     * @return
     */
    public static int[] intersection(int[] nums1, int[] nums2) {
        //0.因为数组最大元素是1000,用一个数组hash表示,
        // hash 下标为元素,值为该元素出现的次数
        int[] hash = new int[1000];
        ArrayList<Integer> result = new ArrayList<>();
        //1.遍历nums1,
        //1.1如果nums1[i]在hash中对应的值为0,就+1
        //1.2如果nums1[i]在hash中对应的值为1,则维持1,(达到去重效果)
        for (int i : nums1) {
            if (hash[i] == 0) {
                hash[i] = 1;
                continue;
            }
            if (hash[i] == 1) {
                hash[i] = 1;
            }
        }
        //2.遍历数组nums2.
        //2.1如果nums2[i]在hash中对应的值为0,那么此元素不可能是公共值
        //2.2如果nums2[i]在hash中对应的值为1,那么表示是公共元素,则+1
        //2.3如果nums2[i]在hash中对应的值为2,那么表示是公共元素,维持2不变,(达到nums2的去重效果)
        for (int j : nums2) {
            if (hash[j] == 1) {
                hash[j] = 2;
                continue;
            }
            if (hash[j] == 2) {
                hash[j] = 2;
            }
        }
        //3.此时hash中值为2的就是公共部分
        for (int i = 0; i < hash.length; i++) {
            if (hash[i] == 2) {
                result.add(i);
            }
        }
        //4.将ArrayList转化为数组
        int[] res = new int[result.size()];
        for (int i = 0; i < result.size(); i++) {
            res[i] = result.get(i);
        }
        return res;
        //4.将ArrayList转化为数组
        //return result.stream().mapToInt(x -> x).toArray();
    }
}

时间复杂度:O(N+M)

空间复杂度:O(1)

202.快乐数

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

题目描述

方法1:HashSet保存之前计算过的数

package daimasuixiangshuati.day06_haxibiao;

import java.util.HashSet;

/**
 * @Author LeiGe
 * @Date 2023/10/11
 * @Description todo
 */
public class KuaiLeShu202_2 {
    /**
     * 方法1-用set记录之前计算过的数的结果
     *
     * @param n
     * @return
     */
    public boolean isHappy(int n) {
        //0.set中记录计算过的数
        HashSet<Integer> record = new HashSet<>();
        //1.进入循环
        while (true) {
            int sum = getNextNumber(n);
            //1.1如果等于1,则是快乐数
            if (sum == 1) {
                return true;
            }
            //1.2查找当前这个数是否在set中
            //1.2.1如果在,说明计算过,要进入死循环了,退出
            if (record.contains(sum)) {
                return false;
            } else {
                //1.2.2如果没有,则加入到set中
                record.add(sum);
            }
            //1.3更新n,进入下一次循环
            n = sum;
        }
    }

    //计算一个数上各个位上的数平方和
    private int getNextNumber(int n) {
        int sum = 0;
        while (n > 0) {
            //取余
            int temp = n % 10;
            sum += temp * temp;
            //计算下一位
            n = n / 10;
        }
        return sum;
    }
}

1.两数之和

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

题目描述

方法1:HashMap

package daimasuixiangshuati.day06_haxibiao;

import java.util.HashMap;

/**
 * @Author LeiGe
 * @Date 2023/10/11
 * @Description todo
 */
public class LiangShuZhiHe1_2 {
    /**
     * 方法2-hashmap方法
     * 用一个map存储:key表示数组中的元素,value表示元素的下标
     *
     * @param nums
     * @param target
     * @return
     */
    public static int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            //另外一个加数
            int sum = target - nums[i];
            //如果另外一个加数在map中,表示找到了
            if (map.containsKey(sum)) {
                return new int[]{i, map.get(sum)};
            }
            //将元素和下标加入到map中
            map.put(nums[i], i);
        }
        return new int[]{-1, -1};
    }
}

时间复杂度:O(N)

空间复杂度:O(N)

3.小结

1.哈希表理论基础

2.当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值