代码随想录算法训练营第7天| ● 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和

发布时间 2023-09-13 16:28:10作者: zz子木zz

454.两数相加 Ⅱ

mydemo--(超时失败)

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int count = 0;
        for(int i=0; i<nums1.size(); i++)
          for(int j=0; j<nums2.size(); j++)
            for(int k=0; k<nums3.size(); k++)
              for(int d=0; d<nums4.size(); d++)
                if(nums1[i]+nums2[j]+nums3[k]+nums4[d] == 0)      
                    count++;
        return count;  
    }
};

时间复杂度O(n^4)

mydemoV2--(卡哥思路)--(成功)

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
    
        unordered_map<int,int> map;
        int count = 0;
        int target = 0;
        //auto iter;

        for(int i=0; i<nums1.size(); i++)
            for(int j=0; j<nums2.size(); j++)
            {
                auto iter = map.find(nums1[i]+nums2[j]);
                
                if(iter == map.end())//如果没找到
                    map.insert(pair<int,int>(nums1[i]+nums2[j], 1));
                else
                    iter->second ++;
            }
        
        for(int i=0; i<nums3.size(); i++)
            for(int j=0; j<nums4.size(); j++)
            {
                auto iter = map.find(target-nums3[i]-nums4[j]);
                if(iter != map.end())
                    count += iter->second;
            }
        
        return count;

    }
};

一个小坑:

auto iter = map.find(a) //没问题
auto iter;	//报错,auto类型必须一开始就初始化
iter = map.find(a); 

卡哥代码

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
    
        unordered_map<int,int> map;
        int count = 0;
        int target = 0;
  
        for(int a : nums1){
            for(int b : nums2)
                map[a+b]++; //ps: map的value有默认初始值,为0
        }

        for(int c : nums3)
            for(int d : nums4){
                if(map.find(target-c-d) != map.end())
                count += map[target-c-d];
            }

        return count;
    }
};

383.赎金信

mydemo--(自己思路)--(成功)

一次就通过!

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        
        unordered_map<int, int> map;  
        for(char b : magazine)
            map[b]++;

        for(char a: ransomNote)
        {
            if(map[a]>0) map[a]--;
            else return false;
        }
        
        return true;
    }
};

时间复杂度O(n)

mydemoV2--(自己思路)--(失败)

用数组来做,大体应该没问题,但是逻辑细节上有问题

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        
        int lenA = ransomNote.size();
        int lenB = magazine.size();

        for(int i=0; i<lenA; i++)
        {
            for(int j=0; j<lenB; j++)
            {
                if(ransomNote[i]==magazine[j])
                {
                    for(; j<lenB-1; j++)
                        magazine[j] = magazine[j+1];
                    lenB--;
                    break;
                }
            }
            return false;
        }

        return true;
    }
};

时间复杂度O(n^3)

卡哥代码

依然是空间换时间的哈希表,但是这里数值不大,分布不散,所以卡哥选择了用数组而不是用map。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};

        if(ransomNote.size()>magazine.size())
            return false;
        
        for(int i=0; i<magazine.length(); i++)
        {
            record[magazine[i]-'a'] ++;
        }

        for(int j=0; j<ransomNote.length(); j++)
        {
            record[ransomNote[j]-'a']--;
            if(record[ransomNote[j]-'a']<0)
                return false;
        }
        return true; 
    }
};

15.三数之和

mydemo--(卡哥思路)--(失败)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        
        sort(nums.begin(), nums.end());
        int len = nums.size();
        int target = 0;
        //unordered_set<int> result;
        unordered_set<unordered_set<int>> result;

        for(int i=0; i<len-2; i++)
        {
            int left=i+1, right=len-1; 
            while(left<right)
            {
                if(nums[i]+nums[left]+nums[right] > target)
                {
                    right--;
                    continue;
                }
                if(nums[i]+nums[left]+nums[right] < target)
                {
                    left++;
                    continue;
                }
                if(nums[i]+nums[left]+nums[right] == target)
                {
                    //if(result.find(vector<int,int,int>{nums[i], nums[left], nums[right]}) != result.end())
                    if( result.find(unordered_set<int>{nums[i], nums[left], nums[right]})  !=  result.end() )
                        result.insert(unordered_set<int>{nums[i], nums[left], nums[right]});
                }
            }
        }

        return vector(result);
    }
};

报错

                    if( result.find(unordered_set<int>{nums[i], nums[left], nums[right]})  !=  result.end() )
                        result.insert(unordered_set<int>{nums[i], nums[left], nums[right]});

这里有些语法问题,还不太会改。

mydemoV2--(卡哥思路)--(成功)

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

    //for(int i=0; i<len-2;i++) //我的demo,没报错
    for(int i=0; i<len; i++)	//卡哥demo
    {
        if(nums[i]>0) break;;
        //if(nums[i] == nums[i-1] && i>0) continue; //报错
        if(i>0 && nums[i] == nums[i-1]) continue;

        int left = i+1;
        int right = len-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(vector<int>{nums[i], nums[left], nums[right]});
                
                //报错
                //while(nums[right] == nums[right-1]) right--;
                //while(nums[left] == nums[left+1])   left++;
                
                /*成功
                while(right>1 && nums[right] == nums[right-1]) right--;
                while(left<len-1 && nums[left] == nums[left+1])   left++;
                */
                /*成功
                while(nums[right] == nums[right-1] && left<right) right--;
                while(nums[left] == nums[left+1] && left<right)   left++;
                */
                while(left<right && nums[right] == nums[right-1]) right--;
                while(left<right && nums[left] == nums[left+1])   left++;
                right--;
                left++;
            }
        }
    }
    
    return result;
    }
};

报错:越下界

这种报错就是说,数组下标出现了负数(因为下标should be unsigned offest)

细节Ⅰ:

//if(nums[i] == nums[i-1] && i>0) continue; //报错
if(i>0 && nums[i] == nums[i-1]) continue;

这段报错是因为顺序写错了,要先判断 i>0 ,不然[i-1]可能会越下界

细节Ⅱ

//报错
//while(nums[right] == nums[right-1]) right--;
//while(nums[left] == nums[left+1])   left++;

这里会出现越下界错误,考虑一个特殊例子:

[0,0,0,0,0,0,0,0]

right会一路向北,当right==0时,[right-1]会越下界

细节Ⅲ

//for(int i=0; i<len-2;i++) //我的demo,没报错
for(int i=0; i<len; i++)	//卡哥demo

我的考虑是对的,因为题目说了len>=3,所以需要考虑 i 最多只能取到[len-3]

不过卡哥这里也是对的,因为:

when  i == len-2;
	left = len-1
    right = len-1
无法通过while(left < right)
i++
---又一轮新的循环---
when i == len-1;
	left = len > right = len-1
无法通过while(left < right)
---循环结束---

卡哥代码

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(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++;
                    
                    right--;
                    left++;
                }
            }
        } 
        return result;
    }
};

18.四数之和

mydemo--(卡哥思路)--(成功)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        int len = nums.size();
        
        for(int k=0; k<len-3; k++)
        {
            if(nums[k]>target && nums[k]>=0)    break;
            //注意先判断k>0,防止越下界
            if(k>0 && nums[k]==nums[k-1])   continue;

            for(int i=k+1; i<len-2; i++)
            {
                //if(nums[k]+nums[i] > target && nums[i]>=0)  return result;;
                if(nums[k]+nums[i] > target && nums[i]>=0)  break;
                //if(i>(k+1) && nums[i] == nums[i+1]) i++;  
                //if(i>(k+1) && nums[i] == nums[i-1]) i++;
                if(i>(k+1) && nums[i] == nums[i-1]) continue;  

                int left = i+1;
                int right = len-1;
                while(left < right)
                {
                    if((long) nums[k]+nums[i]+nums[left]+nums[right] > target) right--;
                    else if((long) nums[k]+nums[i]+nums[left]+nums[right] < target) left++;
                    else{
                        result.push_back(vector<int>{nums[k], 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;
    }
};

问题Ⅰ

//if(i>(k+1) && nums[i] == nums[i+1]) i++; 

这就是经典错误,只考虑到元组和元组不能重复,但没考虑到元组内部的元素可以重复

例子:

问题Ⅱ

//if(i>(k+1) && nums[i] == nums[i-1]) i++;

按照逻辑,不是i++,应当是 continue;

问题Ⅲ

for(int i=k+1; i<len-2; i++)
            {
                //if(nums[k]+nums[i] > target && nums[i]>=0)  return result;;
                if(nums[k]+nums[i] > target && nums[i]>=0)  break;

这里是第二层遍历,进行剪枝后就break到第一层遍历即可,如果是 return的话就会直接终止程序,会漏找元组

问题四

if((long) nums[k]+nums[i]+nums[left]+nums[right] > target) right--;
else if((long) nums[k]+nums[i]+nums[left]+nums[right] < target) left++;

(long)为强制类型转换

(type) expression 

这里,力扣的数据里有一些会很大,相加可能会超出int的范围,所以转换成long进行操作。

卡哥代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++) {
            // 剪枝处理
            if (nums[k] > target && nums[k] >= 0) {
            	break; // 这里使用break,统一通过最后的return返回
            }
            // 对nums[k]去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // 2级剪枝处理
                if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
                    break;
                }

                // 对nums[i]去重
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } else if ((long) nums[k] + nums[i] + nums[left] + nums[right]  < target) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }

            }
        }
        return result;
    }
};