2024.4.17 Day1
哈希表
我们认为,当需要判断一个元素是否出现过的时候,就要考虑哈希法
class Solution {
public:
bool isAnagram(string s, string t) {
/* 分析题目意思,即要判断t中的字符是否都在s中出现过,符合判断一个元素是否出现的场景 */
/* 本体的映射使用数组的形式,字符串必然是26个字母,因此使用一个长度为26的数组 */
int s_record[26] = {0};
int s_size = s.length();
/* 对s中出现过的字符进行标记 */
for(int i=0; i<s_size; i++){ /* a——z对应0——25 */
int index = s[i] - 'a';
s_record[index]++;
}
/* 可以两个数组进行对比,也可以在原数组的基础上进行操作 */
int t_size = t.length(); /* 经验证也可以使用.size() */
for(int i=0; i<t_size; i++){
int index = t[i] - 'a';
s_record[index]--;
}
/* 判断这个数组是否回到了全零 */
for(int i=0; i<26; i++){
if(s_record[i] != 0){
return false;
}
}
return true;
}
};
C++ 获取string字符串长度的三种方法_c++ string长度-优快云博客
使用数组来做哈希的题目,是因为题目都限制了数值的大小
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
/* 求二者交集,可以认为是一个数组中的元素是否在另一个数组中出现过,因此考虑哈希法 */
/* 由于数组的大小不确定,因此不能使用数组进行分析,使用unordered_set */
/* unorder_set和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会 */
/* 不再以键值对的形式存储数据,而是直接存储数据的值 ;
容器内部存储的各个元素的值都互不相等,且不能被修改;
不会对内部存储的数据进行排序 */
unordered_set<int> result_set;
/* 把nums1的数据全部放到unnorder_set中,然后nums2中的元素如果有和unorder_set中数据相同的,则可以放到输出数组中 */
unordered_set<int> nums1_record(nums1.begin(), nums1.end());
for(int num : nums2){
if(nums1_record.find(num) != nums1_record.end()){ /* 找到返回迭代器,失败返回end() */
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
这道题主要是这类语法不太熟,思路看过之后倒是不难
这里unorder_set的特性倒是挺关键的
class Solution {
public:
int get_sum(int num){
/* 从个位数往上看 */
/* num % 10 即取个位数 */
/* num / 10 即去掉个位数 */
int sum = 0;
while(num){
sum += (num%10)*(num%10);
num = num / 10;
}
return sum;
}
bool isHappy(int n) {
/* 题目中说了会无限循环,那么如果有sum重复出现,则一定返回false;
否则,就有一直下去的必要,直到sum = 1 */
/* 定义它是为了记录有没有重复的sum出现 */
unordered_set<int> sum_record;
while(true){
/* 重复计算数的sum,对得到的sum做判断 */
int sum = get_sum(n);
/* 为了记录这个sum有没有重复出现,把它放入unorder_set中 */
if(sum == 1){
return true;
}
else if(sum_record.find(sum) == sum_record.end()){
sum_record.insert(sum);
}
else{
return false;
}
n = sum; /* 更新n用于下一次迭代计算 */
}
}
};
这题问题主要是get_sum函数的写法忘了
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
/* 因为数组大小不确定,不能用数组;
set之恩存放一个key,这里要存放整数和下标,因此不好用set
map可以存放key--value,两个值刚好 */
/* 判断元素是否出现过用key,那么value用来存下标 */
/* 题中说同一个元素不能使用两遍,不是说值相同,而是某个位置相同 */
std::unordered_map<int, int> dataRecord;
/* 因为要下标 */
for(int i=0; i<nums.size(); i++){
/* 判断map中有没有这个元素,若有 */
auto iter = dataRecord.find(target - nums[i]); /* 为了得到下标,所以这样拆开来写 */
if(iter != dataRecord.end()){
return {i, iter->second};
}
// dataRecord.insert(pair(nums[i], i)); /* 这样写GPT说是错的,不过示例能跑过 */
dataRecord.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
这题主要帮我熟悉了迭代器的概念,以及map可以存放键值对,pair函数等
还有return {}的{}可以表示一个空的容器
2024.4.18 Day2
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
/* 本题我也是先看了题解的 */
/* 四个数相加比较复杂,这里采用的是一种类似枚举的方法,但是,四个数太多了,分成两两来看 */
/* 四个数相加等于零,和上一题两数之和等于零的方法类似,于是我们可以写出: */
/* nums1和nums2为一组,nums3和nums4为一组 */
unordered_map<int, int> sumFirstGroup;
for(int n1 : nums1){
for(int n2 : nums2){
/* 我们对两个数求和,并且要记录对应的和以及个数,因此使用map,仅仅要求个数,对顺序无要求,因此用unordered_map */
/* map即键值对,根据键key来查找的,因此两数之和是key,个数是value */
/* sumFirstGroup[n1+n2]代表着值value,即者两个数之和的个数 */
/* 也就是说我们把两数之和当成一个数了 */
sumFirstGroup[n1+n2]++;
}
}
/* 和上一题一样的思路 */
int count = 0;
for(int n3 : nums3){
for(int n4 : nums4){
auto iter = sumFirstGroup.find(0-n3-n4);
if(iter != sumFirstGroup.end()){
// count++; /* 这里不能是加1,因为前面两数之和的个数可能不止一个,因此要加上值 */
count += sumFirstGroup[0-n3-n4];
}
}
}
return count;
}
};
这个题也是先看了解答,我在复现过程中,下面这句:
count += sumFirstGroup[0-n3-n4];一开始写错了,因为记录的数值和可能个数不止一个,要注意下
另外迭代器使用前缀auto
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
/* 这是看题解之前写的:初步认为是搜索,我们可以将magzine中所有字母都送入unordered_map,记录字母并记录个数,然后检索ransomNote */
unordered_map<int, int> mapRecord;
int magzineSize = magazine.size();
for(int i=0; i<magzineSize; i++){
int key = magazine[i] - 'a';
mapRecord[key]++; /* 至此记录了所有的字母 */
}
int ransomNoteSize = ransomNote.size();
if((ransomNoteSize > magzineSize) || (ransomNoteSize < 0) ){
return false;
}
else{
for(int i=0; i<ransomNoteSize; i++){
int key2 = ransomNote[i] - 'a';
auto iter = mapRecord.find(key2);
if(iter != mapRecord.end()){
mapRecord[key2]--;
if(mapRecord[key2] < 0){
return false;
}
}
else{
return false;
}
}
return true;
}
}
};
在前一题的基础上,这种解法是我直接写出来的,算是暴力解法,关键是选对map
看了参考答案,他提供了两种解法:
暴力求解
思路是对magzine中的每个字符,挨个检查其是否在ransomNote里出现过,若出现了则删除ransomNote中的这个字符,最后检查ransomNote的length是否为0
使用.erase方法
哈希法
它没有用map,而是使用长度为26的数组进行记录,因为map的空间消耗比数组大,在数据量大的情况下和数组会有差别
思路和上面基本是一致的,只需要将:
unordered_map<int, int> mapRecord;改为int record[26] = {0}即可,另外会比map少一个检查的步骤,写了就知道了我就不写了
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
/* 这个题目乍一看,要是没说不重复这一个条件......好吧,我不会 */
/* 这道题有点搞,我看了视频之后才来写的。首先进行分析: */
/* 题目中有提到:要找出满足条件,并且“不重复”的三元组,所谓不重复,是说不要出现abc三个数相等的结果,当然对应下标可以是乱的 */
/* 如果使用哈希法,基本还是和前面一样的思路,使用两个for循环挨个得到a和b(当然b可以从a的下一个位置开始循环),最后对c用哈希表(0-(a+b))是否出现在余下的数组中 */
/* 但是这样好像在去重的时候会比较麻烦(我没有写,暂时也没看他的源码) */
/* 视频建议使用双指针法,我下面尝试总结下他的思路 */
/* 三个数,少不了要遍历,对abc三个数来说,用a进行遍历,b和c用两个指针进行查找,a的下标为i,b的下标为left,c的下标为right */
/* 其中,left总是从i的右侧开始,right则总是从数组的末尾开始。先写一段 */
int numsSize = nums.size();
/* 为了更好进行处理,并且体重对下标没有要求,我们先对数组及逆行排序,从小到大的顺序 */
sort(nums.begin(), nums.end()); /* 这样快速排序后虽然改变了原本对应的下标位置,但是对题目要求没有什么大的影响 */
vector<vector<int>> result_set;
for(int i=0; i<numsSize; i++){
/* 既然已经排序了,第一个数不能大于0,否则后面都是整数,不可能等于0 */
if(nums[i] > 0){
// break;
return result_set;
}
/* 我们对abc分得很清楚了,abc是按顺序取值的,按遍历的顺序,可以先对a做去重 */
if(i>0 && nums[i] == nums[i-1]){ /* 一定是要和前一个比较,和后一个比较会有问题(因为后一个可以相同,作为left的形式) */
continue;
}
/* 确定a之后,正式开始处理b和c */
int left = i+1;
int right = numsSize-1;
while(right > left){
if(nums[i] + nums[left] + nums[right] > 0){
right--;
continue;
}
else if(nums[i] + nums[left] + nums[right] < 0){
left++;
continue;
}
else{
/* 加入结果中 */
// result_set.join({i, left, right}) /* 我不熟这种表达,这是我的错误写法 */
result_set.push_back(vector<int>{nums[i], nums[left], nums[right]});
/* 找到一个三元组后,进行去重 */
while(right > left && nums[right] == nums[right-1]){
right--;
}
while(right > left && nums[left] == nums[left+1]){
left++;
}
left++; /* 我开始只写这句,有问题,应该两个指针都要收缩,没考虑清楚(也和我花了太久时间有关系) */
right--;
}
}
}
return result_set;
}
};
这题花的时间比较长,可以说一地鸡毛了
主要问题在,
- 虽然看了讲解,但是开始时对
if(nums[i] > 0)也写错了,只写了if(nums[0] > 0)- 对b,c的去重没找到位置
result_set.push_back(vector<int>{nums[i], nums[left], nums[right]});这类语法不熟后面可以再写一次这题
文章探讨了如何使用C++中的哈希表(如数组和unordered_set)解决字符串判断元素是否出现、数组交集、数列求和判断、字符串字符替换以及数组去重等问题,展示了双指针法在三数之和问题中的应用。
1215

被折叠的 条评论
为什么被折叠?



