代码随想录打卡—day7—【哈希表】— 哈希与双指针

1 哈希题(1)

454. 四数相加 II

 我的第一版TLE 的做法,时间应该是 200*200*200*log(200) 。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) 
    {
        // 思路: 前三个vector 还是遍历 最后一个vector 用unordered_map(猜一下 没有重复的元素)
        //后面跑发现测试样例有重复的 用 multimap
        int num = 0;
        multimap<int,int> x;
        for(int i = 0; i < nums4.size();i++)
            x.insert(pair<int,int>(nums4[i],i));
        for(int i = 0; i < nums1.size();i++)
            for(int j = 0; j < nums2.size();j++)
                for(int k = 0; k < nums3.size();k++)
                {
                    int res = 0 - nums1[i] - nums2[j] - nums3[k];
                    if(x.find(res)!=x.end())
                        num+= x.count(res);
                }
        return num;
    }
};

后来看了carl 讲解.....其实,这道题需要转一下脑筋,我上面的写法是遍历前三个vector 查找最后一个,其实把4个vector割分为2,2一对就可以了先遍历前2个vector,在map存结果,之后遍历后两个vector 查询map。(注意这里的对于map比较简单的写法是——map 后一个int 应该存的是 这个key 在前两个vector的任意两数之和出现的次数!!! AC代码如下:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) 
    {
        // carl的思路
        unordered_map<int,int> x;  // 注意这里的前一个int表示nums1和nums2任意两数和, 后一个int表示这个和的次数
        for(auto i : nums1)
            for(auto j : nums2)
                if(x.find(i+j) != x.end())
                    x[i+j]++;
                else x[i+j] = 1;
        // for(auto iter = x.begin();iter != x.end();iter++)
        //     cout<<iter->first<<' '<<iter->second<<endl;
        int num = 0;
        for(auto i:nums3)
            for(auto j:nums4)
                if(x.find(0-i-j) != x.end()) num+=x[0-i-j];
        return num;
    }
};

2 哈希题(2)【易】

383. 赎金信

是 242.有效的字母异位词 类似的题目,简单

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) 
    {
        int x[26] = {0};
        int y[26] = {0};
        for(auto i:ransomNote)
            x[i - 'a']++;
        for(auto i:magazine)
            y[i - 'a']++;
        for(int i = 0; i < 26;i++)
            if(x[i] >  y[i])return 0;
        return 1;
    }
};

3 表面哈希,实则双指针题【难】

15. 三数之和

 本题难点: 答案中不可以包含重复的三元组【去重】

1. 首先一开始没什么思路,用哈希表的做法感觉很容易超时,看了提示,先sort一下再用多指针的做法===》原因是这里不用返回下标,只用返回数值 所以可以sort ==》 经验:最终结果不用下标只要数值时候可以考虑——双指针和sort的搭配!!!

    于是写出下面这坨,很明显写的不完整 l,r 都没动 + 很明显没有去重【没注意看这个条件】,回头来感觉也能改好??

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // n = 3000 * 3000
        // -4 -1 -1 0 1 1 2
        int l = 0;
        int r = nums.size() - 1;
        vector<vector<int>> x;
        sort(nums.begin(),nums.end());  //升序一下
        while(l < r)
        {
            int mid = r - 1;
            int a = nums[l];
            int c = nums[r];
            while(mid > l)
            {
                int b = 0 - a - c;
                if(nums[mid] != b)mid--;
                else if(nums[mid] )
                else
                {
                    vector<int> tmp;
                    tmp.push_back(nums[l]);
                    tmp.push_back(nums[r]);
                    tmp.push_back(nums[mid]);
                    x.push_back(tmp);
                }
            }

        }
        return x;
    }
};

 .......改失败了,因为 l 和 r 两个同时做 变!量 ,在前进的过程中 l 和 r 的前进有矛盾!!!

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // -4 -1 -1 0 1 2
        int l = 0;
        int r = nums.size() - 1;
        vector<vector<int>> x;
        sort(nums.begin(),nums.end());  //升序一下
        while(l < r )
        {
            if(l != 0  && nums[l - 1] == nums[l])l++;  //去重
            if(r != nums.size() && nums[r] == nums[r + 1])r--;
            int mid = r - 1;
            while(mid > l)
            {
                int b = 0 -  nums[l] - nums[r];
                if(nums[mid] != b)mid--;
                else
                {
                    vector<int> tmp;
                    tmp.push_back(nums[l]);
                    tmp.push_back(nums[r]);
                    tmp.push_back(nums[mid]);
                    x.push_back(tmp);
                    while( mid > l && nums[mid] == nums[mid - 1])mid--;
                    mid--;
                }
            }
            l++;  // ?? 这里有问题 l++ r不变  还是l不变 r-- 还是l++ r-- 都矛盾 
            // r--;
        }
        return x;
    }
};

换成了carl的 i + l + r 三指针的写法(首先将数组排序,然后有一层for循环,i 从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。) + 自己加了个暴力去重,但是TLE

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // -4 -1 -1 0 1 2
        // -4 -1 -1 -1 0 1 2
        // -4 -1 -1 0 1 2 2 
        vector<vector<int>> x;
        sort(nums.begin(),nums.end());
        for(int i = 0; i < nums.size();i++)
        {
            int l = i+1;
            int r = nums.size() - 1;
            while(l < r)
            {
                cout << i << ' ' <<  l  << ' ' << r  << endl;
                if(nums[i]+nums[l]+nums[r] == 0)
                {
                    cout << "----in" << endl;
                    vector<int> a;
                    a.push_back(nums[i]);
                    a.push_back(nums[l]);
                    a.push_back(nums[r]);
                    sort(a.begin(),a.end());
                    if(find(x.begin(),x.end(),a) == x.end())  //去重  超时了!!
                        x.push_back(a);
                    l++;
                    r = nums.size() - 1;
                }
                else if (nums[i]+nums[l]+nums[r] > 0 )
                    r--;
                else l++; 
            }
        }
        return x;
    }
};

 终于AC了!!!(看了carl的视频 照着那个思路写的┭┮﹏┭┮

去重需要在正常扫的时候就完成,【1】对于i的去重,见下图

 【2】对于l 和 r 的去重: 由于第一次找到nums[l] nums[r] 的时候已经收割了, 所以 l 、r 直接走到不等于当下的数值的节点即可。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // -4 -1 -1 0 1 2
        // -4 -1 -1 -1 0 1 2
        // -4 -1 -1 0 1 2 2 
        vector<vector<int>> x;
        sort(nums.begin(),nums.end());
        for(int i = 0; i < nums.size();i++)
        {
            int l = i+1;
            int r = nums.size() - 1;
            if(i!=0 && nums[i] == nums[i-1])continue;  // 对i去重
            while(l < r)
            {
                int sum = nums[i]+nums[l]+nums[r];
                if(sum > 0)
                    r--;
                else if(sum < 0)
                    l++;
                else  // 收获了!!
                {
                    vector<int> a;
                    a.push_back(nums[i]);
                    a.push_back(nums[l]);
                    a.push_back(nums[r]);
                    x.push_back(a);
                    // 对 l 和 r 去重
                    while(l < r && nums[l+1] == nums[l])l++;
                    while(l < r && nums[r-1] == nums[r])r--;
                    l++;
                    r--;
                }
            }
        }
        return x;
    }
};

 4 表面哈希,实则双指针题

18. 四数之和

 和上一题三数之和 的思路一致,多一层FOR循环而已,定两个指针 i 、j  ,移动 l 、 r指针。注意这里不能写当nums[i] > target就break的减枝,因为可能后面三个数相加是负数,越加越小!!如果写减枝需要写成下面这样,我的代码里没有写减枝。

if (nums[k] > target && nums[k] >= 0)

if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) 

没看提示,自己写完的AC代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        // 按照三数之和的思路写一个
        sort(nums.begin(),nums.end());
        vector<vector<int>> out;
        if(nums.size() < 4)return out;  // 注意 size 为1,2,3 的特判!
        for(int i = 0; i < nums.size() - 3;i++)
        {
            if(i != 0 && nums[i-1] == nums[i])continue;  //对于i去重
            for(int j = i + 1; j < nums.size() - 2;j++)
            {
                if(j != i+1 &&nums[j-1] == nums[j])continue;  //对于j去重

                int l = j + 1;
                int r = nums.size() - 1;
                // cout << i << ' ' << j << ' ' << l << ' ' << r << endl;
                while(l < r)
                {
                    if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target)r--;  //这里要开longlong
                    else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target)l++;
                    else  //相同了
                    {
                        out.push_back({nums[i],nums[j],nums[l],nums[r]});
                        while(l < r && nums[l + 1] == nums[l])l++;  //去重
                        while(l < r && nums[r - 1] == nums[r])r--;
                        l++;
                        r--;
                    }
                }
            }
        }
        return out;
    }
};

感受:

1. 第3题 三数之和写的比较麻,写了4版才AC。其他还好

2. 跟上进度当天写的,总共用时3h45min。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值