代码随想录算法训练营Day7|四数之和、赎金信、三数之和、四数之和

[收获1]C++中循环的一种写法是for(int a : A){},其中的int可以是auto自动匹配类型。a是与A数组中的元素相同的变量,最先等于A数组的第一个元素,以此进入循环体。或者理解为从数组A中依次取出元素,赋值给a。

四数之和

原题链接:leetcode454四数之和

[哈希法求解思路]

先看前两个数组nums1和nums2,将不重复的nums1中元素与nums2中元素之和,放进事先定义好的map中,此处使用unordered_map因为元素不重复。

此后再将map中的元素a+b与(0-(c+d))进行比较,相等,则表明a+b与c+d互为相反数,他们四个数的和为0。这里需要对nums3和nums4进行遍历,同时求出c,d两数之和,然后使用find()函数对map中的元素进行寻访,具体代码实现如下

class Solution {
public:
    int fourSumCount(vector& nums1, vector& nums2, vector& nums3, vector& nums4) {
     unordered_mapumap;

     for(auto a : nums1){
         for(auto b : nums2){
             umap[a+b]++;
         }
     }
     int count = 0;
     for(auto c : nums3){
         for(auto d : nums4){
             if(umap.find(0 - (c + d)) != umap.end()){
                 count += umap[0 - (c + d)];
             }
         }
     }
     return count;
    }
};

原题链接:leetcode383赎金信

赎金信

第一遍做的时候,发现基础问题还是很多,比如不知道怎么给字符串通过键盘直接赋值的语句怎么写,对C++的使用有些不熟练。还是考虑用昨天学的哈希表,用大小为26的数组来统计每个字符出现的频次,先遍历第一个字符串,遇到那个小写字母,就通过ASCII码进行转换,找到count计数数组的下标,将索引对应的元素+1,然后遍历第二个字符串,找到计数数组的下标,将对应的元素-1,最后遍历输入的字符串,循环检索每一个该字符串的每一个字符,在count中找到位置,判断是不是0,不管大于还是小于0,都说明他们在两个字符串里数量上不能一一对应起来,都不符合题意,返回false.

求解源代码如下:

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

三数之和

原题链接:leetcode15三数之和

首先想到的当然是三重循环,遍历求解,不过感觉就很……所以不用这个。

[哈希法求解]

定义一个result用来存放三元组,vector>result;

对给定的数组进行排序 ,sort(nums.begin(),nums.end());

用双重循环对三元数组中的前两个数进行求和与去重,然后参考前面一道题的方式,int  c = 0 -  (nums[i] + nums[j] )与第三个数比较,如果相等就表明两数之和与第三个数互为相反数,其和为0,不能忽略对第三个数进行去重,这里可以用erase()函数。

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

    for(int i = 0;i < nums.size();i++)
    {
        if(nums[i] > 0){
            break;
        }
        if(i>0&&nums[i] == nums[i - 1]){
            continue;
        }
        unordered_setset;
        for(int j = i + 1; j < nums.size();j++){
            if(j > i + 2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2]){
                continue;
            }
            int c = 0 - (nums[i] + nums[j]);
            if(set.find(c) != set.end()){
                result.push_back({nums[i],nums[j],c});
                set.erase(c);
            }else{
                set.insert(nums[j]);
            }
        }
    }
    return result;
    }
};

超时了……

[双指针法求解]

求解源代码如下:

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

     for(int i = 0; i < nums.size(); i++)//找到第一元素
     {
         if(nums[i] > 0){  //第一个元素大于0就直接返回,因为此时已经排完序了,一定要有小于0的元素,排序后,默认是升序,第一个元素为正数,一定构不成满足题意的三元组
             return result;
         }
         if(i > 0 && nums[i] == nums[i - 1]){
             continue;
         }
         int left = i + 1;//定义左右指针
         int right = nums.size()-1;
         while(right > left) //左指针一定位于右指针,再进行下列操作
         {
             if(nums[right]+nums[left]+nums[i]>0)right--;//三数之和大于0,要想缩小这个和,就让右指针左移
             else if(nums[i] + nums[left] + nums[right] < 0)left++;//三数之和小于0,要想增大这个和,就让左指针右移,left++
             else{
                 result.push_back(vector{nums[i],nums[left],nums[right]});//如果等于0,就放进result中

                 while(right > left && nums[right] == nums[right - 1])right--;//去重,针对每一次的right只需要考虑它当前的前一个数是否与其相等就可以,因为指针式不断移动的,不需要考虑和它距离超过1的元素的大小是否相等,这个问题。
                 while(right>left && nums[left] == nums[left + 1])left++;

                 right--;//两个指针向区间收缩的方向移动
                 left++;
             }
         }
     }
     return result;
    }
};

一开始对去重很迷惑,看不太懂,看了视频才了解了。

第一个数是在遍历中定位的,如果重复了直接跳过去就好。if( i > 0 && nums[i] == nums[i -1] ){continue;}三元组内部的元素可以是像-1+(-1)+2这种,可以重复,但是两个元组之间不能重复。

后两个数的去重应该安排在找到三元组之后,然后对左右指针进行移动right--;left++;

四数之和

原题链接:leetcode18四数之和

暴力的话,需要四重循环,所以还是换一种方法的好。

参考前一题目,使用双指针求解,先进行双层循环求nums[k]+nums[i],循环内使用left和right指针,找出满足nums[k]+nums[i]+nums[left]+nums[right]=target的四个数,将其作为一个四元组记录进result.

class Solution {
public:
    vector> fourSum(vector& nums, int target) {
     vector>result;
     sort(nums.begin(),nums.end());
     for(int k = 0;k < nums.size(); k++){
         if(nums[k] > target && (nums[k] >= 0 || target >= 0)){
             break;//如果排序后的第一个数是大于目标值target的,同时这第一个数和目标值target中有一个是正数,就可以不再继续了。因为我们要找的是第一个+后三个的和等于target,第一个超了,后面就不用看了。
         }
//第一个数去重

         if(k>0 && nums[k] == nums[k-1]){
             continue;//这个数不能与它前一个数相等
         }
         for(int i = k+1; i< nums.size();i++){//第二个数的定位,从第一个数后面一个开始
             if(nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)){
                 break;//同上一级循环
             }

             if(i > k + 1 && nums[i] == nums[i - 1]){
                 continue;//对第二个数去重
             }
             int left=i+1;
             int right = nums.size()-1;
             while(right > left){
                 if(nums[k] + nums[i] > target -( nums[left] + nums[right])){
                     right--;
                     while(left{nums[k],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;
    }
};

你可能感兴趣的:(数据结构)