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

本文详细讲解了哈希表的原理、哈希函数和解决哈希碰撞的方法,涉及字母异位词的哈希表法、两个数组交集的解决方案,以及Python中的相关实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哈希表(Hash table)part01

  • 哈希表理论基础

  • 242.有效的字母异位词

  • 349两个数组的交集

  • 202快乐数

  • 1.两数之和

 哈希表理论基础

哈希表

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

一般哈希表都是用来快速判断一个元素是否出现集合里。

例如要查询一个名字是否在这所学校里。

要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。

哈希函数

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

哈希碰撞 

拉链法

将冲突的元素放在同一位置的链表中,可以通过同一个标签找到多个元素。

线性探测法

使用线性探测法,一定要保证tableSize大于dataSize。

242.有效的字母异位词

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

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

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

示例 2:

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

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • s 和 t 仅包含小写字母

哈希表法

将字符映射到数组,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。

遍历字符串s,将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。

再遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。

如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。

python:

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False

        record = [0] * 26
        for char in s:
            record[ord(char) - ord('a')] += 1
        for char in t:
            record[ord(char) - ord('a')] -= 1

        for count in record:
            if count != 0:
                return False

        return True

C++

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for(int i=0;i<s.size();i++){
            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){
                return false;
            }
        }
        return true;
    }
};

直接排序法

python

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        return sorted(s) == sorted(t)

C++

#include <algorithm>  // std::sort
#include <string>     // std::string

class Solution {
public:
    bool isAnagram(std::string s, std::string t) {
        if (s.length() != t.length()) {
            return false;
        }

        std::sort(s.begin(), s.end());
        std::sort(t.begin(), t.end());

        return s == t;
    }
};

 使用标准库中的 std::sort 函数对两个字符串进行排序。std::sort 的时间复杂度通常是 O(nlogn)。

Python Counter 方法

  • 使用 Python 的 collections.Counter 类来计数。
  • Counter 会自动为每个字符计数。
  • 如果两个字符串的 Counter 对象相等,则它们是变位词。
  • 这是一种简洁的方法,其时间和空间复杂度与哈希表方法相似。
class Solution(object):
    def isAnagram(self, s: str, t: str) -> bool:
        from collections import Counter
        return Counter(s) == Counter(t)

349.两个数组的集合

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

示例 1:

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

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

C++

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) {
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

unordered_set<int> result_set;

  • 这行代码声明了一个名为 result_setunordered_set<int> 类型的变量。unordered_set 是 C++ 标准库中的一个容器,它用于存储唯一元素的集合。
  • 在这里,result_set 被用于存储最终的结果。由于 unordered_set 自动处理重复的元素(即它只存储唯一的元素),所以使用它来存储结果可以确保结果中不会有重复的数字。

unordered_set<int> nums_set(nums1.begin(), nums1.end());

  • 这行代码创建了一个名为 nums_setunordered_set<int>,并用 nums1 中的元素初始化它。
  • nums1.begin(), nums1.end() 这部分代码是两个迭代器,分别代表 nums1 的起始位置和终止位置。将这两个迭代器传递给 unordered_set 的构造函数,意味着 nums_set 将包含 nums1 中的所有元素。
  • 同样,由于 unordered_set 只存储唯一元素,这也意味着即使 nums1 中有重复元素,nums_set 中每个元素也只会出现一次。

int num : nums2

  • 是 C++ 语言中的范围基于 for 循环的一个示例。这种循环语法是在 C++11 标准中引入的,用于方便地遍历容器(如数组、向量、集合等)中的所有元素。

nums_set.find(num)

调用 find 方法在 nums_set 中查找值为 num 的元素。这个方法返回一个迭代器:

  • 如果找到元素,返回的迭代器指向该元素。
  • 如果未找到元素,返回的迭代器与 nums_set.end() 相同,即指向 nums_set 结束位置的迭代器。

本题规定了整数的长度,也可以用数组做:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        int hash[1005] = {0}; 
        for (int num : nums1) { 
            hash[num] = 1;
        }
        for (int num : nums2) { 
            if (hash[num] == 1) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

python

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return list(set(nums1) & set(nums2))

 

202快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

示例 1:

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

示例 2:

输入:n = 2
输出:false

提示:

  • 1 <= n <= 231 - 1

单独写函数实现sum

使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。

判断sum是否重复出现就可以使用unordered_set。

C++

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;
        }
    }
};

python

class Solution:
    def get_sum(self,n: int) -> int: 
        new_num = 0
        while n:
            n, r = divmod(n, 10)
            new_num += r ** 2
        return new_num
        
    def isHappy(self, n: int) -> bool:        
        record = set()

        while True:
            n = self.get_sum(n)
            if n == 1:
                return True

            if n in record:
                return False
            else:
                record.add(n)

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

使用 map存储结构

其中,key为数据元素,value为数组元素对应的下标

C++

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        map.insert(pair<int,int>(nums[0],0));
        for(int i=1;i<nums.size();i++){
            auto iter = map.find(target-nums[i]);
            if(iter != map.end()){
                return {iter->second,i};
            }
            map.insert(pair<int,int>(nums[i],i));
        }
        return {};
    }
};

声明一个 unordered_map<int, int>,这个哈希表用于存储数组中每个元素及其索引。键为元素的值,值为对应的索引。

pair<int, int>(nums[i], i)

  • 这创建了一个 pair 对象,其中第一个元素(键)是 nums 数组中当前索引 i 处的值(nums[i]),第二个元素(值)是当前的索引 i

auto iter

  • 这是使用自动类型推导声明的迭代器 iterauto 关键字让编译器自动推断 iter 的类型,它会是 unordered_map<int, int>::iterator 类型。这个迭代器用于指向找到的元素,或者如果没有找到,则指向 mapend()

iter->second 

  • iter 是一个指向 unordered_map 中元素的迭代器。在您的代码中,unordered_map 存储的是键值对(pair),其中键(first)是数组中的一个数字,值(second)是该数字的索引。

python

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        num_to_index = {}
        for i, num in enumerate(nums):
            complement = target - num
            if complement in num_to_index:
                return [num_to_index[complement], i]
            num_to_index[num] = i
        return []

使用 enumerate 函数遍历 nums 数组。enumerate 提供了数组的索引和值。

暴力法

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i, j]
        return []

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值