day7 代码训练营 主要是哈希表和双指针法和几数之和问题

第一题四数之和

一开始还是只能想到说是四重循环 但是我估计肯定不行

另外这个题的主要思想就是用两个循环去解决4个循环的暴力解法 

我一上来想的是去用四个multimap去存储ABCD 但那样是不行的 因为:

使用四个multimap存储A、B、C和D的元素,然后进行四个循环,其实就是一种暴力解法。对于每一个a、b、c和d的组合,你都需要检查它们的和是否为0。这种方法的时间复杂度是O(n⁴),因为你需要遍历A、B、C和D中的所有元素四元组。

虽然multimap可以按照键(key)进行排序并快速查找特定的键,但这并不能改变你需要遍历所有四元组的事实。只有当你需要查找或删除特定键的元素时,multimap的特性才能发挥作用。

也就是说 根本没用 那个只是你去查找特定元素的时间开销会小 但是你还是需要去遍历的话 那么时间复杂度就没变 还是O(n4)

继续说这个题 其关键就是 用两个unordered map 然后key去存值 然后value去存数量 也就是有几个相同的和 这样就能把时间复杂度减小到O(n2)

还有一个就是比较神奇的点 虽然有重复 但是还是可以用unordered_map去处理 因为你可以让value 去存储重复的次数

另外你还要注意一个问题 就是你在外层循环和内层循环不能同时int i=0 应该区分变量

其他没什么 还是很简单的小题:

class Solution {

public:

    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {

        unordered_map <int,int> map1;

        int sum1=0,sum2=0;     

        for(int i=0;i<nums1.size();i++){

            for(int j=0;j<nums2.size();j++){

                sum1=nums1[i]+nums2[j];

                if(map1.find(sum1)!=map1.end()){

                    auto iter=map1.find(sum1);

                    iter->second++;

                }

                else{

                    map1.insert({sum1,1});

                }

            }

        }

        int count=0;

         for(int i=0;i<nums3.size();i++){

            for(int j=0;j<nums4.size();j++){

                sum2=nums3[i]+nums4[j];

                if(map1.find(-sum2)!=map1.end()){

                    auto iter=map1.find(-sum2);

                    count=count+iter->second;

                }

                

            }

        }

        return count;

    }

};

另外还有一个就是你用map 可以直接用 map[a+b]++; 这种操作 它的意思是先去查a+b在不在 如果在 就value加1 如果不在就创建一个 value为0的 为什么是value加1呢 因为在std::unordered_map中,键(key)是唯一的,并且在键值对创建后,键是不可变的。你不能直接修改键,但是你可以修改与键关联的值。所以当你使用umap[a + b]++时,你修改的是与键a + b关联的值,而不是键本身。

总而言之就是 key是不能改的 所以肯定不能加1

vector <int> num[26]={0}; 注意不能这么写 因为容器不是数组 你能先vector <int> num[26];这么写 然后再去循环赋值

另外数组不能用xxx.size() 因为不是容器

赎金信 非常简单:一遍过 没什么好说的

class Solution {

public:

    bool canConstruct(string ransomNote, string magazine) {

       int num[26]={0};

       for(int i=0;i<magazine.size();i++){

           num[magazine[i]-'a']++;

       }

       for(int i=0;i<ransomNote.size();i++){

           num[ransomNote[i]-'a']--;

       }

       for(int i=0;i<26;i++){

           if(num[i]<0)

           return false;

       }

        return true;



 

    }

};

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

能用数组就不用别的!

第三题 三数之和 

还是要注意 你用花括号可以用容器的初始化 但是你以后还想添加的时候 必须得用push_back()方法

而外你也不能用insert() 因为你需要给一个迭代器 比如说insert(it,10) 这样 这和set或者map不同 因为他们都是排了序的 或者是之前一直用的unordered_set 这种 你直接输数值就行 因为它是之前按插入的值去给你索引的 

另外像这种二维数组 你直接push_back后面的括号里还得有一个对象 你可以直接用花括号 不然你就得std::vector<int> temp; temp.push_back(nums[i]); temp.push_back(nums[left]); temp.push_back(nums[right]); result.push_back(temp); 这么做 就很没效率 就是先创建一个temp 然后push_back temp 最后再push_back 这个result

另外这个题还有一个最重要的一点 就是你去重的时候 是

  if (nums[i] == nums[i + 1]) {
                continue;
            }

还是

 if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

上面那种就不行 因为这样就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。

还有一个你就是理解错了  i是不能移动的 而要移动的是left和right 因为你要保证i是不变的 你才能在这个基础上去遍历left和right数组

那么你在找到后 肯定是要同时移动两个指针的 因为数组已经被排好序了 你要是只移动left或者right的话 那么还是不行 肯定不是0

而且每次你内循环的时候都要把left和right归位

另外在c++中:

如果在循环体中声明并初始化一个变量,那么在每次循环开始时,这个变量都会被重新声明和初始化。这就意味着这个变量的生命周期仅限于当前循环迭代。

这就是说,每次循环迭代结束后,该变量就会被销毁,然后在下次循环迭代开始时,再次被创建并初始化。

另外还有一个问题是:因为一开始是0 你如果不写i>0的话 就会访问到非法的下标 因此你还有写上i>0

 if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

另外就是找到答案的时候也要去去重right和left  没找到答案不用去重 因为反正也没找到  有什么好去重的 不过我觉得也可以去重

另外你在去重后 还是要left++和right-- 不得不说这道题我只能用恶心来形容

class Solution {

public:

    vector<vector<int>> threeSum(vector<int>& nums) {

        vector<vector<int>> result;

        int i=0,left,right=nums.size()-1,count=0;

        sort(nums.begin(),nums.end());

        for(;i<nums.size();i++){

            left=i+1;

            right=nums.size()-1;

            if(i>0&&nums[i]==nums[i-1])

            continue;

            while(left<right){

                if(nums[left]+nums[i]+nums[right]==0){

                    result.push_back({nums[i],nums[left],nums[right]});

                    while(left<right&&nums[left]==nums[left+1])

                    left++;

                    while(right>left&&nums[right]==nums[right-1])

                    right--;

                    left++;

                    right--;

                }

                else if(nums[left]+nums[i]+nums[right]>0)    

                {

                    right--;

                }

                else if(nums[left]+nums[i]+nums[right]<0)

                {

                    left++;

                }

            

            }

        }

        return result;

    }

};

非常恶心  有很多很多很多的小细节 

总结如下 

1 i的去重 

2 left和right的去重 

另外在元素不相等的时候这两个去重完全没必要加 因为编译器还是会一点点的执行下去。

另外这个四数之和也是 其实还是双指针 而不是哈希表 这个思路要对 相当于在三数之和上又加上了 一个指针就完事了

注意啊注意啊 这个四数之和的双指针法并不是最优时间复杂度 因为这样的时间复杂度是O(n的立方) 而哈希表的时间复杂度是O(n的平方) 但是来说空间复杂度要低很多 因为这样的空间复杂度是O(1) 而那种的复杂度是O(n的平方)因为空间复杂度是指除了输入和输出所需的空间之外的额外空间。输入和输出的空间需求通常不计入空间复杂度。

其实这个题非常简单 就是在三数之和外面再套一层for循环 做一个剪枝处理 以后再多数之和也是一个道理。

跟那个一开始的四数之和不一样 那个是四个独立的数组  这个是一个数组 因此要保留双指针不变 相当于是两个数 然后外面套循环 多一个就多套一层 做剪枝处理、

!!!!!!注意注意先排序!!!!!!!

!!!!!!另外left要初始化为i+1而不是i!!!!!!!

!!!!!!注意注意!!!!! 这个

if(nums[i]==nums[i-1])

                continue;

并不是说i和numsi-1相等都要去重  因为在i等于k+1的时候 每一个i都是对应的不同的k 所以i相同有可能k不同 

比如说 k -1 i 0 和k 1 i 2 就不需要去重 但是k-1 i0 i0 第二个i就需要去重了 所以:总结起来 第一个元素都不需要去重 即使是k 因为第一个元素根本就不会重复!!!

!!!!注意溢出问题 四个数以上相加就会溢出问题!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值