居然有休息日!
目录
哈希表理论基础
建议:大家要了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。 这句话很重要,大家在做哈希表题目都要思考这句话。
文章讲解:代码随想录
关于哈希表,或者说散列表的知识已经在上学期的课中系统地学习过,这里就不详述了。就列一些知识点:哈希函数、哈希碰撞、拉链法、线性探测法、哈希结构。
242.有效的字母异位词
建议: 这道题目,大家可以感受到 数组 用来做哈希表 给我们带来的遍历之处。题目链接/文章讲解/视频讲解: 代码随想录
题目:
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
思路:
我的思路是先判断它的长度是否一致,然后再判断其中各个字符的数量是否一致,因为字符串也是可以看作是一种数组,我利用sort函数将它进行排序,如果这两个字符串互为字母异位词,那经过排序的字符,应该就是相同的,于是我写下了如下代码:
class Solution {
public:
bool isAnagram(string s, string t) {
if(s.length()!=t.length()) return false;
sort(s.begin(),s.end());
sort(t.begin(),t.end());
if(s==t) return true;
else return false;
}
};
这道题也有它的哈希表做法。简单来说就是从a到z,26个字符,进行一个编号(可以通过其asc码的方式进行编号),把它存储在一个26个位空间的数组中,然后将两个数组进行比较,代码具体操作是加上前一个字符串的字符数,再减去后一个字符串的字符数,这样可以遍历这个数组,若所有的元素都为零,就可以判断两个字符串是字母异位词。具体代码如下:
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0};
for (int i = 0; i < s.size(); i++) {
// 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
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) {
// record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return false;
}
}
// record数组所有元素都为零0,说明字符串s和t是字母异位词
return true;
}
};
349. 两个数组的交集
建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议大家忽略这个条件。 尝试去使用set。题目链接/文章讲解/视频讲解:代码随想录
题目:
给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
思路:
#include <vector>
class Solution {
public:
bool judge(vector<int> v,int in){
for(int i = 0;i< v.size() ; i++ ){
if(v[i]==in) return true;
}
return false;
}
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> r = {};
for(int i = 0; i< nums1.size() ;i++ ){
for(int j = 0;j< nums2.size() ; j++ ){
if(nums1[i]==nums2[j]){
if(judge(r,nums1[i])) break;
else{
r.pop(nums1[i]);
break;
}
}
}
}
return r;
}
};
而写这道题目,主要是为了学会使用一种哈希数据结构:unordered_set,这个数据结构可以解决很多类似的问题。因为这道题目没有限制数值的大小,无法使用数组来做哈希表。具体来讲,就是先让第一个数组转化成unordered_set结构,它可以使里面的数据不再重复,再让它与第二个数组进行比较,就可以得出结果。具体代码如下:
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) {
// 发现nums2的元素 在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
202. 快乐数
建议:这道题目也是set的应用,其实和上一题差不多,就是 套在快乐数一个壳子题目链接/文章讲解:代码随想录
题目:
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
思路:
老实说,不会。
学习后明白,关键是要看求和过程中,求和的值是否重复出现,可以判断他是否在无限循环。至于判断这个是否重复出现,就可以用到上面所介绍的unordered_set结构,代码如下:
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;
}
}
};
1. 两数之和
建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。建议大家先看视频讲解,然后尝试自己写代码,在看文章讲解,加深印象。
题目链接/文章讲解/视频讲解:代码随想录
题目:
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
思路:
首先是暴力解法:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return {};
}
};
若用哈希表进行解题,可以选择std::unordered_map。
map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。
在遍历数组的时候,只需要向map去查询是否有和目前遍历元素匹配的数值,如果有,就找到的匹配对,如果没有,就把目前遍历的元素放进map中,因为map存放的就是我们访问过的元素。
具体代码如下:
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++) {
// 遍历当前元素,并在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 {};
}
};