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

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

  • 哈希表理论基础
    • 哈希表
    • 哈希函数
    • 哈希碰撞
      • 拉链法
      • 线性探测法
    • 常见的三种哈希结构
    • 总结
  • 242.有效的字母异位词
    • :eyes:题目总结:eyes:
  • 349. 两个数组的交集
    • :eyes:题目总结:eyes:
  • 202. 快乐数
    • :eyes:题目总结:eyes:
  • 1. 两数之和
    • :eyes:题目总结:eyes:
  • :balloon:心得收获

哈希表理论基础

哈希表

Hash table(散列表)

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

哈希函数

在这里插入图片描述

哈希碰撞

在这里插入图片描述
一般哈希碰撞有两种解决方法, 拉链法和线性探测法

拉链法

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

线性探测法

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

常见的三种哈希结构

  1. 数组
  2. set(集合)
  3. map(映射)
    在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:
    在这里插入图片描述
    std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加
    在这里插入图片描述
    std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的
    在这里插入图片描述

总结

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法,但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找

242.有效的字母异位词

题目链接
视频讲解
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词

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

需要把字符映射到数组也就是哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25,再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了,那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作,那么最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false,最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
?

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
            record[s[i] - 'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        // record数组所有元素都为零0,说明字符串s和t是字母异位词
        return true;
    }
};

?题目总结?

时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)

349. 两个数组的交集

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

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

输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序使用数组来做哈希的题目,是因为题目都限制了数值的大小,而这道题目没有限制数值的大小,就无法使用数组来做哈希表了,而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费

std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set
在这里插入图片描述
?

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

那有同学可能问了,遇到哈希问题我直接都用set不就得了,用什么数组啊,直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的,不要小瞧 这个耗时,在数据量大的情况,差距是很明显的
本题后面 力扣改了 题目描述 和 后台测试数据,增添了 数值范围:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
所以就可以 使用数组来做哈希表了, 因为数组都是 1000以内的
?

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        int hash[1005] = {0}; // 默认数值为0
        for (int num : nums1) { // nums1中出现的字母在hash数组中做记录
            hash[num] = 1;
        }
        for (int num : nums2) { // nums2中出现话,result记录
            if (hash[num] == 1) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

?题目总结?

输出结果中的每个元素一定是唯一的,可以不考虑输出结果的顺序

202. 快乐数

题目链接
编写一个算法来判断一个数 n 是不是快乐数
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数如果 n 是快乐数就返回 True ;不是,则返回 False

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了所以这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止判断sum是否重复出现就可以使用unordered_set
?

class Solution {
public:
    // 取数值各个位上的单数之和
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

?题目总结?

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要

1. 两数之和

题目链接
视频讲解
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标

输入:nums = [2,7,11,15], target = 9
输出:[0,1]

我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适,map中的存储结构为 {key:数据元素,value:数组元素对应的下标}
在这里插入图片描述
在这里插入图片描述
?

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            // 遍历当前元素,并在map中寻找是否有匹配的key
            auto iter = map.find(target - nums[i]); 
            if(iter != map.end()) {
                return {iter->second, i};
            }
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
};

?题目总结?

本题其实有四个重点:
1.为什么会想到用哈希表
2.哈希表为什么用map
3.本题map是用来存什么的
4.map中的key和value用来存什么的

?心得收获

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了文章来源地址https://uudwc.com/A/wNXAW

原文地址:https://blog.csdn.net/weixin_68724534/article/details/131761304

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年07月26日 12:54
kubeasz 安装K8S 错误解决
下一篇 2023年07月26日 12:55