2024年10月30日(双指针算法)

一.和为s的两个数字:

1.题目描述:

这个题目就是找出两个数,这两个数的和是目标值,找到其中一对就可以返回了。

2.算法原理:

方法一:

暴力枚举的策略:

就是两层for循环,固定一个数,然后另一个数去遍历整个数组,去加加然后找出是否有值加

起来两数之和是目标值的。

伪代码:

for(i=0;i<n;i++){
  for(j=i+1;j<n;j++){
     check(nums[i]+nums[j]==t);
 }
}

这个暴力枚举的方法的时间复杂度就是O(n^2).所以去编写代码这个时间复杂度肯定是过不了的。

方法二:

任何题目第一个想到的方法就是暴力枚举的方法,但是这个题目还有一个注意的点是,这个数

组是递增的顺序,所以我们还可以优化这个暴力枚举。在昨天我们利用了数组的单调性解决了一个

问题,这个题目也可以利用这个单调性来解决。

这里我们来举个例子:

就利用这两个双指针,然后分为三种情况,和大于目标值,和小于目标值,和等于目标值。

看图中这种情况left所指的数加上right所指的数,加起来小于目标值,而中间这些数都是比right所

指的数小的,所以2不需要和这些数相加,因为肯定会小于目标数的,所以此时left此时指的数,不

会是最终结果,那么left++就行,再换一个区域去找即可:

此时left加加,最后到7,加起来和还是小于目标值,所以left继续移动:

此时加起来和就超过了目标值,那么right所指的数就不对了,那么就right--,往前找小的数,让和

变小:

此时我们发现加起来的和正好为目标值,那么此时就找到了,就可以直接返回了。

这种算法原理,我们判断一次就可以干掉好多种情况,少掉好多次判断,时间复杂度就大大缩减,

这种时间复杂度就变为O(n)。

3.代码展示:

class Solution {
public:
    vectot<int>towSum(vector<int>& nums,int target){
        int left=0,right=nums.size()-1;
        while(left<right){
            int sum=nums[left]+nums[right];
            if(sum>t){
                right--;
            }else if(sum<t){
                left++;
            }
            else{
                return {nums[left],nums[right]};
            }
        }
       //如果编译器此时没有找到就也需要一个返回路径,所以随便返回两个数即可
        return {-1,-1};
    }
};

二.15. 三数之和 - 力扣(LeetCode)

1.题目描述:

这里题目的意思就是三个数的下标不一样,并且加起来要等于0那么这一组就叫三元组。

这里我们通过题目给的例子来分析一下题目的意思:

在题目给的例子,我们自己列出来了三种情况,但题目只返回了两种,因为我们列的第一种和第三

种虽然顺序不一样,但是因为里面的内容一样的,所以最后要舍去,所以接下来写代码也要考虑

到这一点。

2.算法原理:

方法一:暴力解法

就是用三层循环来找加起来的和为0的数。

但是找很简单,我们还有一个重要的步骤就是去重,那么如何去去重呢,一开始想的是,把

和加起来为0的三元组里面的元素都排个序,然后再去看看,哪个重了,但是这样就更加麻烦了,

所以这里我们想到直接上来就给这个无序的数组先排序,然后再来暴力枚举可能会好一点。

例如:

这样我们看,这样排完序后再去找,直接就是一模一样的,不需要再去每一个三元组都去排序那么

麻烦,我们在C++学过set容器可以去重,直接放进去就可以。这样时间复杂度就是O(n^3),因为

只有这三重循环有时间复杂度,排序和去重都有相对于的函数和容器去弄,就相当于是一个常数的

时间复杂度。

方法二:

方法二也就是在暴力枚举的基础上去进行优化,减少时间复杂度。这里我们在暴力解法中,

已经讲数组排成有序的,那么既然是有序的,我们就想到了利用双指针算法,就去利用单调性去

来找。在和为s这道题我们是利用双指针解决的,这么是三个数相加和为0,那么我们也可以利用双

指针算法去解决这个问题。我们来看一个例子:

我们在暴力枚举里面是先固定一个数然后去找,那么在这里我们也先固定一个数,去找:

那么既然这样我们就固定第一个数去找,那么问题就转变成了在我画的这个区间去找两个数和为4

的数,就转变成了和为s的两个数字的那个题目的解法。正好-4+4=0.那么此时再利用双指针去找,

这里去找还有个可以优化,如果固定的数大于0,那么找的两数之和肯定不会是负数,因为是从小

到大的顺序排列的数组

在处理细节上不漏掉能构成的数组的情况比较好解决,那么就是在和为s的两个数字的题目中,我

们是找到了就返回,那么现在我们找到了还不能返回,要让left和right同时减减,这样才能做到情

况不漏。所以找到一种结果不能立马返回,不能停,两边都缩小区间去继续找。

既然不漏掉情况这个细节已经处理完毕了,去重怎么办呢,因为我们已经排好序了:

比如我已经在里面找到了0,4两个数据,因为已经排好序了,所以相等的值就在后面,如果后面的

值相等,我们就++,--过去,不考虑重复的数。既然里面去重了,外面固定的数也要去重。这样才

能完美去重。例如第一个-4枚举完了,完美就不枚举下一个-4,就直接跳过去。

当我们处理完这两个细节,我们还要避免越界,因为如果所有元素都相等,一直跳,跳出界就也会

报错

当这两个细节处理好,那么这个题目就可以完美得到解决,最重要的就是这两个细节的处理。

3.代码展示:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
         vector<vector<int>> ret;
        //排序:
        sort(nums.begin(),nums.end());
        
        //利用双指针解决问题
        int n=nums.size();
        for(int i=0;i<n;)//固定一个数
        {
            //如果固定的数大于0,那么找的两数之和肯定不会是负数,因为是从小到大的顺序排列的数组
            if(nums[i]>0){
                break;
            }
            int left=i+1,right=n-1,target=-nums[i];
            while(left<right){
                if(nums[left]+nums[right]>target){
                    right--;
                }else if(nums[left]+nums[right]<target){
                    left++;
                }
                else{
                    ret.push_back({nums[i],nums[left],nums[right]});
                    left++,right--;//不漏的细节处理
                    //区间里的去重操作 left<right是防止越界访问
                    while(left<right&&nums[left]==nums[left-1]) left++;
                    while(left<right&&nums[right]==nums[right+1]) right--;
                }
            }
            //去重 固定的那个数
            i++;
            while(i<n&&nums[i]==nums[i-1]) i++;
        }
         return ret;
     }
};

这个代码有非常多的细节需要去注意,所有建议多看这个解题过程。

相关推荐
tinker在coding22 分钟前
Coding Caprice - Linked-List 1
算法·leetcode
唐诺5 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华5 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_5 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子5 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨6 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡6 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客6 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin6 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin