代码随想录算法训练营day7

3 阅读4分钟

454四数相加: 要点,通过分而治之得思想,将四个数组分成两对,这样就在暴力解法的基础上从n的4次方改进成2个n平方遍历。 使用hash表unordered_map构造map用于存储和快速查找,还要注意cpp中迭代器的使用:

public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        //使用map存储nums1,nums2之和,然后计算nums3,nums4之和,在map中查找相应值
        int count=0;
        unordered_map<int,int> calmap;
        for(int i=0;i<nums1.size();i++)
        for(int j=0;j<nums2.size();j++){
           int sum1=nums1[i]+nums2[j];
            calmap[sum1]++;
        }
        //计算统计次数
        for(int i=0;i<nums3.size();i++)
        for(int j=0;j<nums4.size();j++){
           auto it = calmap.find(0-nums3[i]-nums4[j]);
                if (it != calmap.end()) {
                    // 找到补数:累加该补数出现的次数
                    count += it->second;
                }
        }
        return count;
    }
};

15.三数之和。 此处如果采用hash表做法会异常复杂,因此考虑到为从同一数组中取三数求和,可以在排序的基础上使用双指针法进行数组的快速遍历。

重点难点: 1.如何设计双指针,i的每次移动,都需要重置j,k,并根据和的大小移动指针 2.如何去重,三数中每次i移动时当出现相同元素时就会出现重复,也就是说外层循环和内层循环都要去重一次 3.如何存储目标数组,设置vector数组,并且要熟悉迭代器使用

public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //先对nums进行排序
         int n = nums.size();
        
      
        
        // 核心步骤1:排序(去重+双指针的基础)
        sort(nums.begin(), nums.end());
        //设置存储向量
        vector<vector<int>> group;
          // 边界处理:数组长度小于3,直接返回空
        if (n < 3) return group;
       
       
        //开始循环遍历,注意最左侧数字只多只能是倒数第三个元素
        for(int i=0;i<nums.size()-2;i++){
           //剪支判断
           if(nums[i]>0) break;
            if (i > 0 && nums[i] == nums[i-1]) continue;
           //循环判断数组元素是否满足条件
            int j=i+1;
            int  k=nums.size()-1;
           while(j<k){
            
             
             if(nums[i]+nums[j]+nums[k]<0){
                j++;
            }
            else if(nums[i]+nums[j]+nums[k]>0){
                k--;
            }

            else if(nums[i]+nums[j]+nums[k]==0){
               
                group.push_back({nums[i], nums[j], nums[k]});
                 //去重判断,注意,这里去重判断需要同自身前一元素相互比较
                // 去重2:跳过j的重复值(避免重复三元组)
                    while (j < k && nums[j] == nums[j+1]) ++j;
                    // 去重3:跳过k的重复值(避免重复三元组)
                    while (j < k && nums[k] == nums[k-1]) --k;
                    
                    // 找到有效解后,同时移动j/k继续查找
                    ++j;
                    --k;
            }
           
           }
          
        }
        return group; 
    }
};

18.四数之和

public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //先对nums进行排序操作;
         sort(nums.begin(), nums.end());
       
        vector<vector<int>> result;
        //排除小于4的数组
          if (nums.size()< 4) return result;
        //在三数之和的基础上增加一层外层循环
        for(int i=0;i<nums.size()-3;i++){
            //执行剪枝操作,因为使用target,无法判断target的正负以及nums数组的正负分布情况
             if(nums[i]>target && target>0 &&nums[i]>0){
                break;
             }
             //一层循环去重
             if(i>0 && nums[i]==nums[i-1]) continue;
             //二层循环为三数之和
             for(int j=i+1;j<nums.size()-2;j++){
                long long two_sum=nums[i]+nums[j];
                //三数之和的剪支操作
                if(two_sum>target&&target>0&&nums[j]>0){
                    break;
                }
                //二层循环去重
                if(j>i+1&&nums[j]==nums[j-1]) continue;
                //定义左右双指针
                int left=j+1;
                int right=nums.size()-1;
                //三层循环为双指针移动
                while(left<right){
                    //循环判断
                    //四数之和小于目标,左指针右移
                    long long four_sum=two_sum+nums[left]+nums[right];
                    if(four_sum<target){
                        left++;
                    }
                    //四数指针大于目标,右指针左移
                    else if(four_sum>target){
                        right--;
                    }
                    //记录符合要求四数
                    else if(four_sum==target){
                         result.push_back({nums[i],nums[j],nums[left],nums[right]});
                           // 去重:跳过left的重复值
                        while (left < right && nums[left] == nums[left+1]) left++;
                        // 去重4:跳过right的重复值
                        while (left < right && nums[right] == nums[right-1]) right--;
                        
                        // 移动指针继续查找
                        left++;
                        right--;

                    }
                 
               
                }
               
             }
        }
        return result;

    }
};

要点分析: 1.思路,在三数之和的基础上增加外层循环 2.剪支操作的条件区分,要注意到剪支操作需要考虑负数形式 3.每一层都要执行去重操作,注意内层循环的去重操作循环在循环条件内部 4.执行int加法计算时要注意超出取值范围问题,必须要设置long long 变量