知识提要:哈希表
今天开始是哈希表的应用,之前没有写过这类题,先简单了解下常用知识
哈希表(散列表):可以根据关键字的值直接进行查询与访问的数据结构
通常通过映射函数将关键字直接对应到表中的某个位置,加快查找速度
作用:一般哈希表都是用来快速判断一个元素是否出现集合里。
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组 通常适用:数值较小,范围较小
- set (集合) 通常适用:数值大,分散
- map(映射) 通常适用:下标 元素
能用数组就用数组,更直接更快。
c++常用语法——unordered_set
(1)引入头文件:
#include <unordered_set>
(2)begin(),end()函数
begin负责返回指向第一个元素(或第一个字符)的迭代器;end负责返回指向容器(或string对象)“尾元素的下一位置”的迭代器,也就是说,end返回的这个迭代器指向的是容器的一个本不存在的“尾后”元素,
(3)find()
s.find(2) //查找key为2的键值对是否存在 ,若没找到则返回s.end()
if(s.find(2)!=s.end()) //如果s中存在key为2的键值
(4)insert()
s.insert(x)//插入值为x的元素
242.有效的字母异位词
讲解:代码随想录
题目描述:
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
思路:小写字母的字符串,出现的次数想到哈希表,因为字符串由小写字母构成,数值小 范围小,想到哈希表的数组。
通过for 循环来统计字符串 s 和 t 中字母出现的次数,并存储在数组 中。每个字母对应数组 a 的一个位置,通过字符减去 'a' 的 ASCII 码得到在数组中的索引。循环遍历 s 和 t 的每个字符,将对应位置的数组元素加一和减一。(一开始的想着 字符串s用一个数组统计每个字母出现次数,字符串t再用一个数组统计,然后两个数组进行对比,但是觉得太麻烦了)
class Solution {
public:
bool isAnagram(string s, string t) {
//长度不等 一定不是
if (s.length() != t.length()) {
return false;
}
//定义一个数组,长度26,初始值0
vector<int>a(26, 0);
//统计字符串 s 和 t 中字母出现的次数,并存储在数组 a 中
for (int i = 0; i < s.length(); i++) {
a[s[i] - 'a']++;
a[t[i] - 'a']--;
}
//如果符合题意,经过上面操作后这个数组每个数还是0
for (int i = 0; i < 26; i++) {
if (a[i] != 0) {
return false;
}
}
return true;
}
};
当字符是小写字母时,其 ASCII 码值是顺序递增的。例如,'a' 的 ASCII 码值是 97,'b' 的 ASCII 码值是 98,以此类推。假设我们有一个字符 c,我们可以通过减去 'a' 的 ASCII 码值来获得它在数组中的索引。
349.两个数组的交集
讲解:代码随想录
题目描述:给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
这个是直接看视频写的,不知道set,map都咋用的.......只知道点数组
思路:交集的元素是在两个数组中都出现的,判断一个元素是否在集合中出现,想到哈希表,数组中这个数无特殊说明情况下可能很大,或者很分散,这就比较适合用set,最后结果是没有重复元素的,所以最终使用unordered_set
(1)将数组nums1处理成unordered_set的集合
(2)遍历nums2,看看集合中是否能找到当前的值
(3)如果找到了,将结果插入到结果集合中
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//将nums1数组处理成set
unordered_set<int> nums_set(nums1.begin(), nums1.end());
//结果插入到res中,使用unodered_set定义 res中的数已经去重
unordered_set<int> res_set;
//循环将依次遍历 nums2 中的每个元素,
//将当前元素的值赋给 num,然后执行循环体内的代码块
for (int num : nums2) {
//如果在nums_set中找到了当前的nums2的值
if (nums_set.find(num) != nums_set.end()) {
//将这个值插入到res_set中
res_set.insert(num);
}
}
//返回的是数组类型
return vector<int>(res_set.begin(), res_set.end());
}
};
注意:(1)for循环中的写法用于遍历容器中的元素。其中,nums2
是一个容器(比如数组、向量等),num
是循环过程中每次迭代时获取的当前元素的名称。循环将依次遍历 nums2
中的每个元素,将当前元素的值赋给 num
,然后执行循环体内的代码块。这种语法使得遍历容器变得更加简洁和易读。
(2)最后返回值类型是数组
1.两数之和
题目:力扣1.两数之和
讲解:代码随想录
题目描述:
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
思路:
很容易想到暴力法
(1)暴力法:
定义个数组存放下标。两个for循环,外层遍历数组,内层从当前的后一位开始遍历,寻找与当前值相加等于目标值的值,找到满足条件的两个数后,给数组赋值将结果返回。如果未找到,则返回两个元素值均为-1。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res(2,-1);
for (int i = 0; i < nums.size(); i++) {
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] + nums[j] == target) {
res[0] = i;
res[1] = j;
}
}
}
return res;
}
};
(2)哈希法:
定义一个集合来存放遍历过的元素,遍历数组,在集合中查找(target-当前值)是否被遍历过
这道题不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,所以需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。
注意:
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//定义map,存放被遍历过的元素
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 {};
}
};
重点:为什么用哈希表的map
map是用来干什么的
map的key和value是干什么的
map是用来存放遍历过的元素
key对应元素,value对应下标。因为我们要判断的是某个元素是否出现过,所以key是元素
202.快乐数
题目:力扣202.快乐数
讲解:代码随想录
题目描述:
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
快乐数可能很快乐,但是我不是很快乐
获取到数字的每一位并把每一位进行平方计算再求和的结果压入要哈希表中,之后再将获取到的和在哈希表中进行查找,如果查找到了,说明该值出现过就直接返回false,如果没有找到,并且当和为1的时候,说明该数是快乐数,返回true。
先贴个代码,还不是很懂
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;
}
}
};