(6/60)哈希表理论基础、有效的字母异位词、两个数组的交集、快乐数、两数之和

哈希表理论基础

定义

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

特点是查询时间复杂度为O(1)。

哈希函数

哈希表存储过程:键---映射--->数值(索引)中,完成这个映射过程的函数。

哈希碰撞

哈希函数映射到同一位置时称为哈希碰撞。

解决方案有两种:

  1. 拉链法。在原位置上向后接链表存储。
  2. 线性探测法。在表中线性遍历下去寻找空位。

常见哈希结构

  1. 数组
  2. 集合(set)
  3. 映射(map)

C++提供的set数据结构

集合 底层实现 是否有序 数值是否可以重复 能否更改数值 查询效率 增删效率
std::set 红黑树 有序 O(log n) O(log n)
std::multiset 红黑树 有序 O(logn) O(logn)
std::unordered_set 哈希表 无序 O(1) O(1)

C++提供的map数据结构

映射 底层实现 是否有序 数值是否可以重复 能否更改数值 查询效率 增删效率
std::map 红黑树 key有序 key不可重复 key不可修改 O(logn) O(logn)
std::multimap 红黑树 key有序 key可重复 key不可修改 O(log n) O(log n)
std::unordered_map 哈希表 key无序 key不可重复 key不可修改 O(1) O(1)

(从上往下依次为:不可重复、可重复、不可重复且无序)

红黑树实现的key是有序的,但只能增删,不可修改。

总结

  1. 快速判断元素是否存在于集合内时,要想到哈希法。

  2. 本质是空间换时间


有效字母异位词

leetcode:242. 有效的字母异位词

排序法

思路

很简单的想法,排序完之后对比俩字符串是否相等。

复杂度分析

时间复杂度:O(NlogN)。两个排序,单个排序O(NlogN)。

空间复杂度:O(1)。

注意点

代码实现

class Solution {
public:
// 排序法
bool isAnagram(string s, string t) {
sort(s.begin(),s.end());
sort(t.begin(),t.end());
if(s == t)
return true;
else
return false;
}
};

哈希法

思路

要判断某些元素出现次数是否相等,想到哈希法。

元素数量确定且小(26个字母),所以用数组的数据结构。

  1. 遍历一个字符串,对映射位置的元素++;再遍历另一个字符串,对映射位置的元素--
  2. 遍历映射数组,看元素是否还为初始化的值,全为初始值则两字符串为有效字母异位词,否则不是。

复杂度分析

时间复杂度:O(N+M)。两个并列循环,N、M分别是s、t的长度,加一个常数次的循环。

空间复杂度:O(1)。开了常数个(26)空间的数组。

注意点

  1. 数组初始化

    1. 使用花括号 {} 初始化:

      cppCopy Codeint arr[3] = {1, 2, 3};

      这种方式可以用于静态数组和动态数组的初始化,也可以用于多维数组的初始化。

    2. 不使用花括号 {} 初始化:

      cppCopy Codeint arr[3] = {0}; // 所有元素都初始化为0
      int arr[3] = {}; // 所有元素都初始化为0
      int arr[] = {1, 2}; // 数组长度自动推断为2

      这种方式也适用于静态数组和动态数组的初始化,但是不适用于多维数组的初始化。

    3. 使用数组下标逐个赋值:

      cppCopy Codeint arr[3];
      arr[0] = 1;
      arr[1] = 2;
      arr[2] = 3;

      这种方式可以用于静态数组和动态数组的初始化,也可以用于多维数组的初始化。

    4. 使用循环语句逐个赋值:

      cppCopy Codeint arr[3];
      for (int i = 0; i < 3; i++) {
      arr[i] = i + 1;
      }

      这种方式可以用于静态数组和动态数组的初始化,也可以用于多维数组的初始化。在多维数组的初始化时,需要嵌套多层循环。

  2. 数组不能直接.size()获取长度。可以通过sizeof(arr)/sizeof(arr[0])的方式获取数组长度。

代码实现

class Solution {
public:
// 哈希法
bool isAnagram(string s, string t) {
// 用一个size为26的数组来映射字符出现次数
int hash[26] = {0}; // *数组初始化方式
// s中出现的字符对应位置++
for(int i = 0;i < s.size();i++){
hash[ s[i] -'a' ]++; // 细节,字母减去'a'刚好就映射到了0~25的数组下标
}
// t中出现的字符对应位置--
for(int j = 0;j < t.size();j++){
hash[ t[j] -'a' ]--;
}
// 如果最终数组元素全为0,则是字母异位词
for(int k = 0;k < 26;k++){
if(hash[k] != 0)
return false;
}
return true;
}
};

两个数组的交集

leetcode:349. 两个数组的交集

哈希法(集合)

思路

输出结果唯一且无序,直接念unordered_set身份证号了,所以返回结果用无序集转vector。

中间过程,交集如何找:

  1. 另设一个集合,遍历存入数组1的值。
  2. 遍历数组2,看数组2元素是否已经存在于中间集,如果是则将元素存入结果集。

复杂度分析

时间复杂度:O(N)。

空间复杂度:O(N+M)。N是nums1的大小,是nums1、nums2的交集大小。

注意点

  1. vector有构造函数可以用迭代器。

代码实现

class Solution {
public:
// 哈希法(集合)
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 结果集
unordered_set<int> temp_set; // 中间集(过程)
for(int num : nums1){
temp_set.insert(num);
}
for(int num : nums2){
if(temp_set.find(num) != NULL){
result_set.insert(num);
}
}
return vector<int>(result_set.begin(),result_set.end());
}
};

哈希法(数组)

思路

同集合法。已知数组长度<=1000时可以用数组来做映射。

复杂度分析

时间复杂度:O(N)。

空间复杂度:O(N)。N是两数组交集大小。

注意点

略。

代码实现

class Solution {
public:
posted @   Tazdingo  阅读(2359)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示