代码随想录过程记录

文章探讨了如何使用C++中的哈希表(如数组和unordered_set)解决字符串判断元素是否出现、数组交集、数列求和判断、字符串字符替换以及数组去重等问题,展示了双指针法在三数之和问题中的应用。


2024.4.17 Day1

哈希表

我们认为,当需要判断一个元素是否出现过的时候,就要考虑哈希法

image-20240417211728184
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长度-优快云博客

使用数组来做哈希的题目,是因为题目都限制了数值的大小


image-20240417213935902
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的特性倒是挺关键的

C++常用语法——unordered_set_c++ unordered_set-优快云博客


image-20240417214518605
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函数的写法忘了


image-20240417221435793
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

image-20240418194856277
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


image-20240418203047395
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少一个检查的步骤,写了就知道了

    我就不写了


image-20240418205755405
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]});这类语法不熟

后面可以再写一次这题



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值