代码随想录day06|242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和

提示:DDU,供自己复习使用。欢迎大家前来讨论~


哈希表

学习

一、哈希表的基础理论

1. 哈希表的使用

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

哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

2. 哈希碰撞:拉链法和线性探测

3. 常见的三种哈希结构

  • 数组
  • set集合
  • map映射
image-20240805150653033

二、题目

题目一:242.有效的字母异位词

思路:

定义一个记录26个字母的数组,先遍历第一个字符串,进行字母个数的累加,再遍历第二字符串,进行字母个数的缩减。

最后判断,如果每一个字符的在数组中对应的个数。

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0}; // 使用初始化列表,数组的每个元素都将被初始化为 0
        for (int i = 0; i < s.size(); i++) {
            record[s[i] - 'a']++;
        }

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

        for (int i = 0; i < 26; i++) { //这里要遍历26个字母
            if (record[i] != 0)
                return false;
        }
        return true;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

题目二:349.两个数组的交集

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

所以这个题目,在没有给定限制条件的情况下,使用set是最好的。

直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。这个耗时在数据量大的情况下,差距也很明显。

三种常用的数据结构:

  • std::set
  • std::multiset
  • std::unordered_set

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

1.使用set:
这个一次性记住有点难。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> nums_set(nums1.begin(), nums1.end());

        for (int num : nums2) {
            // find这个成员函数,找到了返回指向该元素的迭代器,如果没找到就会返回指向end的迭代器
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num); //每次insert和find一个数值,需要进行一个哈希运算,并开辟新的空间。
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};
  • 时间复杂度: O(n + m) m 是最后要把 set转成vector
  • 空间复杂度: O(n)

2.使用数组:

力扣改了 题目描述 和 后台测试数据,增添了 数值范围:

  • 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());
    }
};
  • 时间复杂度: O(m + n)
  • 空间复杂度: O(n)

题目三:202.快乐数

使用哈希表进行寻找,计算各位数字的平方和,为了防止元素重复,使用unordered_set, 如果有重复,说明无限循环了,return false 结束。

反之,继续循环求平方和,直至sum = 1。

  • 取数值的每一位数字。求和(单独写一个函数,自己掌握一下。)
  • 判断是不是快乐数
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;
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};
  • 时间复杂度: O(logn),对于一个数字n,其位数是O(log n)
  • 空间复杂度: O(logn)

题目三:1.两数之和

本题有四个重点:

  • 为什么会想到用哈希表:找一个元素是否出现过,或者元素是否在集合中出现过。
  • 哈希表为什么用map:map可以用来记录key ,value对。
  • 本题map是用来存什么的:存放遍历过的元素。
  • map中的key和value用来存什么的:key是用来存值,value用来存下标。 {key:数据元素,value:数组元素对应的下标}
  • 为什么使用unordered_map: key不需要有序,此时使用unordere_map效率更高查找和增删效率均为O(1).

代码细节:

在C++中的映射容器(如 std::mapstd::unordered_map)中,每个元素是一个键值对,其中包含两个部分:

  • first:表示键(key),它是唯一的,用于在映射中标识元素。
  • second:表示值(value),与键相关联的数据。

通过 iter->first 来访问键部分,通过 iter->second 来访问值部分。这里的 -> 运算符是用于访问指针指向对象的成员的。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map; //用来记录遍历过的元素
        for (int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target - nums[i]); //找到和为target的另一个元素
            if (iter != map.end()) {
                return {iter->second, i};
            }
            map.insert(pair<int, int>(nums[i], i));//map的添加pair<type,type>(,)
        }
        return {};
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

总结

  • 学习使用了哈希表三种结构的用法

    • 数组:以其快速访问的特性而著称,适用于存储固定大小的数据集合。
    • set:一个不允许重复元素的集合,保证了元素的唯一性
    • map:一种键值对集合,允许我们通过键快速访问对应的值。
  • 哈希表的基本操作,

    • find():用于查找特定元素是否存在于哈希表中,并返回指向该元素的迭代器

    • end():返回一个指向哈希表末尾的迭代器,常用于遍历操作的终止条件。

    • insert():用于向哈希表中添加新的元素。对于 map,使用 insert() 函数时,通常需要提供一个 pair 类型的参数,例如 map.insert(std::make_pair<key_type, value_type>(key, value))

    • auto 是 C++ 中的类型推导关键字,它允许编译器根据初始化表达式自动确定变量的类型

今天的题目基本都是使用了两个集合或者数组,进行对比和对应的操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值