242.有效的字母异位词
题目描述:
给定两个字符串 *s*
和 *t*
,编写一个函数来判断 *t*
是否是 *s*
的字母异位词。
**注意:**若 *s*
和 *t*
中每个字符出现的次数都相同,则称 *s*
和 *t*
互为字母异位词。
示例一:
输入: s = "rat", t = "car"
输出: false
示例二:
输入: s = "anagram", t = "nagaram"
输出: true
提示:
1 <= s.length, t.length <= 5 * 104
s 和 t 仅包含小写字母
解题思路:
- 关键词提取:每个字符、次数一致
- 哈希表解法:检索是否重复、检索次数,可以优先考虑哈希表法
- 首先判断两个数组长度是否一致
- 一致的情况下,建立一个数组模拟哈希表,原因是字符对应的数值有限,不会有溢出风险
- 同步检索两个数组,一个数组往哈希表中添加元素,一个数组往哈希表中删除元素
- 检查哈希表,如果两个数组的字符种类与个数一致,哈希表的元素的值都为零
代码如下:
class Solution {
public:
bool isAnagram(string s, string t) {
// 假如两个数组长度不一致
if(s.size() != t.size()) {
return false;
}
// 建立一个数组,模拟哈希表
int stack[27] = {0};
// 同步遍历两个数组,数组s往哈希表中增加计数,数组t往哈希表中删除元素计数
for(int i = 0; i < s.size(); i++) {
stack[s[i]-'a']++;
stack[t[i]-'a']--;
}
// 检查哈希表中每个元素是否为0,若两个数组的字符种类与数量一致,就是0
for(int i = 0; i < 27;i++) {
if(stack[i] != 0) {
return false;
}
}
return true;
}
};
总结:
- 二刷的时候,已经是一气呵成,一下子就把代码写出来,并且一次就AC。
1)解题:每个字符、出现次数。
2)不熟悉的时候,想的就是,暴力解法,对于每一个字符,都做一次遍历,并且统计次数。然后,再嵌套一个循环,在另一个数组那遍历该字符,并且统计次数。
3)但是,现在脑海中会有哈希表法。题目的关注点在于种类和次数,假如我有一个表格,遍历一遍,将所有的元素都填进去,而且将次数统计出来。那,两个数组,我就用两个表格,最后比对两个表格就可以。
4)再优化一下,就是,将两个表格合并为一个表格。先将第一个数组的元素填进去。再将第二个数组的元素从表格上擦除。看最后表格中还有没有多余的元素就可以。 - 代码随想录提供的就是哈希表法,关键点如下:
1)采用数组做哈希表。
2)根据字符大小,确认数组的大小。
3)分别遍历两个字符数组。
4)检查哈希表中元素的个数。
349.两个数组的交集
题目描述:
给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例一:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例二:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
解题思路:
- 关键词提取:交集、结果唯一、不考虑顺序
- 哈希表法:
- 思路和上一题一样,采用数组做哈希表,提示中有规定数值大小,所以不需要调用哈希表的库函数
- 这题新增一个难点就是要去重,和上题稍微有一点不一样。所以这道题要先将一个数组的元素填充进哈希表。
- 再遍历另一个数组,从哈希表中将对应的元素删除掉,要单独做一个整体删除的动作。
- 所以一共做两次循环,第一次循环将元素填入表中,第二次循环从表上查找是否有对应元素,并且做好元素删除动作。
- 找对应元素不需要暴力遍历,直接检索哈希表索引即可。
代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 申请一个返回数组,记录元素
int len = 0;
vector<int> result(1001, 0);
// 申请一个数组做哈希表
int record[1001] = {0};
// 将目标数组的值填充进哈希表中
for(int i = 0; i < nums1.size(); i++) {
record[nums1[i]]++;
}
// 在哈希表中,检索当前数组的值是否存在
for(int j = 0; j < nums2.size(); j++) {
// 检索到哈希表中有值,保存到返回数组红姑娘,同时,将哈希表中的值清零,代表已取用
if(record[nums2[j]] > 0) {
record[nums2[j]] = -1;
result[len++] = nums2[j];
}
}
// 对返回数组进行一次缩小
vector<int> tmp(len, 0);
for(int i = 0; i < len; i++) {
tmp[i] = result[i];
}
return tmp;
}
};
总结:
- 一刷和二刷都采用的这个方式。没有想着自己去编一个哈希表的函数,这个太折磨人了。写个简单的题目,用C语言,编写哈希表的各个操作,要一大堆代码。
1)难点一:注意到限制条件,才能采用数组来做哈希表。否则容易溢出。
2)难点二:去重的解决方案,脑海中老是告诉自己,要排序,才能做去重。但是实际上,只需要将哈希表中的元素删除,就可以达到去重的目的。哈希表的key是数字,value是出现的次数,将value清零,就是将元素删除。 - 代码随想录提供的是哈希表函数,需要用到对应的容器。
1)我是只会C语言的,没怎么学过C++,这次也是借着写博客的这个过程,基于算法题去应用一下C++语言,从实践出发,反推自己去研究基础理论知识。所以我没法基于C++语言,对题目考查的知识点,作出回应。更多的是对于算法以及C语言的知识点,作出的解释。请见谅。
2)看了下,C++语言还是好呀,蛮方便的,可以用容器去实现哈希,可以用库函数去做插入而不用考虑数组大小,可以用库函数去去重,而不用自己去排序、循环检索去重。有兴趣的小伙伴可以看下解析。
3)代码随想录讲解视频:https://www.bilibili.com/video/BV1ba411S7wu
202.快乐数
题目描述:
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例一:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例二:
输入:n = 2
输出:false
提示:
1 <= n <= 231 - 1
解题思路:
- 关键词提取:每个位置的平方、替换为所有位置的平方和、会无限循环
- 解法:
- 需要封装一个函数,取每个位置的数值
- 将每个位置的数值求平方,并且累加
- 将值存放到哈希表中
- 假如某个数值出现过第二次,说明进入死循环
代码如下:
class Solution {
private:
// 计算每个数字的平方,并且求和
int getNum(int n) {
int sum = 0;
while(n) {
// 取个位的数字
int tmp = n % 10;
// 累加每个位置数字的平方
sum += tmp * tmp;
// 将取完的数字删除掉
n = n / 10;
}
return sum;
}
public:
bool isHappy(int n) {
unordered_set<int> set;
int sum = 0;
while(1) {
sum = getNum(n);
// 假如平方和为1,即符合题目要求
if(sum == 1) {
return true;
}
// 检索哈希表中是否有sum值,有的话意味着进入无限循环
if(set.find(sum) != set.end()) {
return false;
}
// 将值插入到哈希表中
else {
set.insert(sum);
}
// 更新n值
n = sum;
}
}
};
总结:
- 二刷,可以快速写出解法,但是不知道怎样转换成代码,痛苦。
- 代码随想录提供的是哈希表函数,需要用到对应的容器。
1)难点一:需要注意怎么取各个位置的数字。
2)难点二:需要理解题目中无限循环的含义,需要转换成一个平方和出现第二次。
1.两数之和
题目描述:
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例一:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例二:
输入:nums = [3,2,4], target = 6
输出:[1,2]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
解题思路:
- 关键词提取:目标值、两个数求和、下标、答案唯一、元素不重复
- 解法:
- 采用哈希表做,键对中,键值是整数值,参数是数组下标
- 遍历数组,将目标值与当前值作差,在哈希表中寻找差值
- 若找到差值,就返回数组下标,否则就将当前元素添加进入哈希表中
代码如下:
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++) {
// 在哈希表中检索目标值与当前值的差值
auto iter = map.find(target - nums[i]);
// 若找到差值,就返回下标
if(iter != map.end()) {
return {iter->second, i};
}
// 若找不到差值,就将当前值与下标插入到哈希表中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
总结:
- 二刷,可以快速写出解法,但是不知道怎样转换成代码,痛苦。暴力法虽好,但是容易超时。采用C写哈希表,需要调用库函数,蛮痛苦的一个过程。
- 代码随想录提供的是哈希表函数,需要用到对应的容器。
1)难点一:需要注意到哈希表的容器选择,需要key和value。
2)难点二:需要注意到key存的是元素值,value存的是下标。