6.1+6.2——day11+day12——计科大一0基础面向工作式学习

学习概要:

代码随想录:哈希表

        6.四数相加Ⅱ:leetcode454

本题解题步骤:

  1. 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
  2. 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
  3. 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
  4. 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
  5. 最后返回统计值 count 就可以了

方法一:

这是我自己想的使用两个unordered_map来解决问题,但是呢逐个访问unordered_map的语法我不会,是网上查的

时间复杂度o(n*n)

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int n=nums1.size();
        int sum=0;
        unordered_map<int,int> sum12;
        unordered_map<int,int> sum34;
        
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(sum12.find(nums1[i]+nums2[j])!=sum12.end()){
                    sum12[nums1[i]+nums2[j]]++;
                }else{
                    sum12[nums1[i]+nums2[j]]=1;
                }
            }
        }

        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(sum34.find(nums3[i]+nums4[j])!=sum34.end()){
                    sum34[nums3[i]+nums4[j]]++;
                }else{
                    sum34[nums3[i]+nums4[j]]=1;
                }
            }
        }

        for(auto it = sum12.begin();it!=sum12.end();it++)
        {
            if(sum34.find(-(it->first))!=sum34.end()){
                sum+=sum12[it->first]*sum34[-(it->first)];
            }
        }

        return sum;
    }
};

方法二:

优化了空间复杂度的常数因子,并且避开了迭代器逐个访问unordered_map里面所有键值对的步骤

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int n=nums1.size();
        int sum=0;
        unordered_map<int,int> sum12;
        
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                sum12[nums1[i]+nums2[j]]++;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(sum12.find(-(nums3[i]+nums4[j]))!=sum12.end()){
                    sum+=sum12[-(nums3[i]+nums4[j])];
                }
            }
        }
        return sum;
    }
};

        7.赎金信leetcode383

方法一:自己想出来的耶耶耶,依旧是使用unordered_map,不过使用的键值对类型是<char,int>,分别统计每个字符出现的个数,最后保证ran中需要的字符小于等于mag中的字符(好像可以优化,我自己再优化看看,见方法二)

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<char,int> mag;
        unordered_map<char,int> ran;
        for(int i=0;i<ransomNote.length();i++)
        {
            ran[ransomNote[i]]++;
        }

        for(int i=0;i<magazine.length();i++)
        {
            mag[magazine[i]]++;
        }

        for(auto it=ran.begin();it!=ran.end();it++)
        {
            if(it->second > mag[it->first]){
                return false;
            }
        }
        return true;
    }
};

 方法二:

优化思路同上提一样,减少了一个unordered_map的使用,并且避免了使用迭代器去访问unordered_map的每一个元素。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<char,int> mag;
        for(int i=0;i<magazine.length();i++)
        {
            mag[magazine[i]]++;
        }

        for(int i = 0;i<ransomNote.length();i++)
        {
            mag[ransomNote[i]]--;
            if(mag[ransomNote[i]]<0){
                return false;
            }
        }

        return true;
    }
};

方法三:

因为题目说只有小写字母,那可以采用空间换取时间的哈希策略,用一个长度为26的数组来记录magazine里字母出现的次数。

然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。

依然是数组在哈希法中的应用。

一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

就是使用数组映射

a->0

b->1

以此类推。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int hash_map[26]={0};
        for(int i=0;i<magazine.length();i++)
        {
            hash_map[magazine[i]-'a']++;
        }
        for(int i=0;i<ransomNote.length();i++)
        {
            hash_map[ransomNote[i]-'a']--;
            if(hash_map[ransomNote[i]-'a']<0){
                return false;
            }
        }
        return true;

    }
};
  • 时间复杂度: O(m+n),其中m表示ransomNote的长度,n表示magazine的长度
  • 空间复杂度: O(1)

8.三数之和leetcode15

方法一:

是可以用哈希法解决的,具体见代码随想录网站题解和力扣上题解。——我暂时放过这种方法了,我去寻找其他方法吧,但是到时候重新做的时候要把这种方法搞懂。you know?

方法二:

使用set<vector<int>>容器,将每一个三元组排序后再插入,这样就可以利用set的自动去重功能,达到去重的效果。方法是自己想的,但是过程中一些语法是问AI的。但是好像时间复杂度有点过于高了,过了311/314个测试点,那个最后执行的输入有点太复杂了,时间超限了

好好看好好学,里面有很多语法细节

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> result;
        unordered_map<int,int> hash;
        for(int i=0;i<nums.size();i++)
        {
            hash[nums[i]]=i;
        }
        for(int i=0;i<nums.size();i++)
        {
            for(int j=0;j<nums.size();j++)
            {
                if(hash.find(-(nums[i]+nums[j]))!=hash.end()){
                    int target=-nums[i]-nums[j];
                    if(hash[target]!=i&&i!=j&&j!=hash[target]){
                        vector<int> tmp={nums[i],nums[j],-nums[i]-nums[j]};
                        sort(tmp.begin(),tmp.end());
                        result.insert(tmp);
                    }
                }
            }
        }
        return vector<vector<int>> (result.begin(),result.end());
    }
};

把代码给AI后确实发现很多问题,如下:

代码分析

时间复杂度
  • ​双重循环​​:外层循环遍历数组 (O(n)),内层循环遍历数组 (O(n)),组合为 ​​O(n²)​
  • ​哈希查找​​:每次查找操作平均 O(1),最坏 O(n)
  • ​排序三元组​​:每次对固定大小的三元组排序 (O(1)),但执行 O(n²) 次
  • ​set插入​​:红黑树插入时间复杂度为 O(log k),k 为当前元素数量
  • ​总体时间复杂度​​:​​O(n² log n)​​(log n 来自 set 插入)//时间复杂度确实有点过于高了
空间复杂度
  • ​哈希表​​:存储 n 个元素 → ​​O(n)​
  • ​结果集​​:最坏情况需存储 O(n²) 个三元组 → ​​O(n²)​
缺陷
  1. ​索引覆盖​​:哈希表只存储最后出现的索引,当存在重复元素时可能漏解//这个是必须要解决的并且还没想到什么好方法解决
    示例[0,0,0],哈希表只记录最后一个 0 的索引
  2. ​重复解​​:依赖排序三元组 + set 去重,代价高
  3. ​冗余计算​​:双重循环产生大量重复检查
  4. ​边界无效​​:当元素不足 3 个时仍执行计算

方法三:

排序+双指针+剪枝

剪枝的操作确实蛮繁琐的也很难想完全,还是要自己多画图多想,多实践,还是要自己分析具体输入的时候为什么输出会出错,而不是一直依赖AI;

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>0){return result;}
            if(i>0&&nums[i]==nums[i-1]){continue;}
            int left=i+1;
            int right=nums.size()-1;
            while(left<right){
                if(nums[i]+nums[left]+nums[right]>0){
                    right--;
                }else if(nums[i]+nums[left]+nums[right]<0){
                    left++;
                }else{
                    result.push_back({nums[i],nums[left],nums[right]});
                    while(left<right&&nums[right]==nums[right-1]){right--;}
                    while(left<right&&nums[left]==nums[left+1]){left++;}
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

方法四:排序+半剪枝

在方法三的基础上巧妙的使用set<vector<int>>来实现不遗漏的去重,这样好像会增加时间复杂度,不过也可以通过所有测试点就是了

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> result;
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>0){return vector<vector<int>> (result.begin(),result.end());}
            if(i>0&&nums[i]==nums[i-1]){continue;}
            int left=i+1;
            int right=nums.size()-1;
            while(left<right){
                if(nums[i]+nums[left]+nums[right]>0){
                    right--;
                }else if(nums[i]+nums[left]+nums[right]<0){
                    left++;
                }else{
                    result.insert({nums[i],nums[left],nums[right]});
                    //while(left<right&&nums[right]==nums[right-1]){right--;}
                    //while(left<right&&nums[left]==nums[left+1]){left++;}
                    right--;
                    left++;
                }
            }
        }
        return vector<vector<int>> (result.begin(),result.end());
    }
};

方法五:双指针+排序+完全不剪枝也是可以过的

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> result;
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();i++)
        {
            //if(nums[i]>0){return vector<vector<int>> (result.begin(),result.end());}
            //if(i>0&&nums[i]==nums[i-1]){continue;}
            int left=i+1;
            int right=nums.size()-1;
            while(left<right){
                if(nums[i]+nums[left]+nums[right]>0){
                    right--;
                }else if(nums[i]+nums[left]+nums[right]<0){
                    left++;
                }else{
                    result.insert({nums[i],nums[left],nums[right]});
                    //while(left<right&&nums[right]==nums[right-1]){right--;}
                    //while(left<right&&nums[left]==nums[left+1]){left++;}
                    right--;
                    left++;
                }
            }
        }
        return vector<vector<int>> (result.begin(),result.end());
    }
};

        8.四数之和leetcode18

方法1;排序+双指针+剪枝

我觉得比三数之和麻烦太多了,特别是剪纸操作,还有一些代码的逻辑其实我还不是很清楚,明天复习三数之和和四数之和,要都重新做一遍,特别是四数之和的剪纸操作!!

在AI和代码随想录的帮助下也勉强AC(我自己写的代码,去交给AI改错,以及和代码随想录比对,但是一些不一样的我不觉得我有错,但是修改了那个地方就是有一些测试点过不了,所以明天我要搞懂这些不一样的地方到底有什么问题)

AC代码如下

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        int n=nums.size();
        if(n<4){return {} ;}
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();i++)
        {
   
            if(i>0&&nums[i]==nums[i-1]){continue;}
            if(target>=0&&nums[i]>target){return result;}


            for(int j=i+1;j<nums.size();j++)
            {
                if(j>i+1&&nums[j]==nums[j-1]){continue;}
                if(target>=0&&nums[i]+nums[j]>target){break;}
                int left=j+1;
                int right=nums.size()-1;
                
                while(left<right){
                    if((long)nums[i]+nums[j]+nums[left]+nums[right]>target){
                        right--;
                    }else if((long)nums[i]+nums[j]+nums[left]+nums[right]<target){
                        left++;
                    }else{
                        result.push_back({nums[i],nums[j],nums[left],nums[right]});
                        while(left<right&&nums[left]==nums[left+1]){left++;}
                        while(left<right&&nums[right]==nums[right-1]){right--;}
                        right--;
                        left++;
                    }
                }
            }
        }
        return result;   
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值