基础算法--双指针算法

文章目录

  • 什么是双指针算法
  • 例题
    • 1.移动零
    • 2.复写零
    • 3.快乐数
    • 4.盛最多水的容器
    • 5.有效三角形的个数
    • 6.三数之和
    • 7.四数之和

在这里插入图片描述

什么是双指针算法

通常我们讲的双指针就是用两个指针,两个指针可以是快慢指针,解决成环的问题,也可以是指向收尾的两个指针,来减小时间复杂度。双指针算法里的指针也不止是指针,在数组中也可以是数组元素的下标,这里双指针是一种思想,并不是单单指的是指针。
接下来我们用几道例题来看看双指针算法。

例题

1.移动零

题目:移动零

在这里插入图片描述

样例输出输入:

在这里插入图片描述

这道题要我们将所有零移到数组的末尾并且不改变数组的的非零元素的相对位置,并且有一个前提就是不能开辟新的数组,对原数组进行操作。
解法一:暴力解法
思路:两层循环,一层循环找零,一层循环移动零到最后一个零的位置。
解法二:双指针
思路:首先对于上面这个题可以这样理解,可以把他划分为两个大区间,一个已经排除过零的区间和一个未排除过零的区间,已排除过零的区间又可以划分为非零区间和一个只含零的区间所以这三个区间。
在这里插入图片描述
所以我们可以用一个指针指向非零与零的区间的分界线,再用一个指针对数组进行遍历,如果遇到零就继续++,因为零本来就该在最后,如果遇到非零数就交换非零区间和零区间的分解线的数,这里分界线取的是第一个零的位置。这样,当我们遍历到最后一个数之后,未划分区间就没有了,只剩下非零区间和零区间了。
代码展示:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int cur=0;
        int dest=-1;
        while(cur<nums.size())
        {
            if(nums[cur]!=0)
            {
                swap(nums[cur],nums[dest+1]);
                dest++;
                cur++;
            }
            else
            {
                cur++;
            }
        }
    }
};

2.复写零

题目:复写零

在这里插入图片描述

样例输入输出:

在这里插入图片描述

题目的意思就是让我们把数组当中的零在零之后复写但是复写之后数组的长度不变,所以后面数向后移动,所以最后就得到了样例输出。

思路:
首先我们先用异地操作模拟一下,开辟一个新的数组,然后进行拷贝,用两个指针,一个指针对原来的数组进行遍历,一个指针对新开辟的数组进行遍历,如果原来的数组遇到非零先判断新开辟数组是否越界,然后进行拷贝,两个指针同时向后移动,如果遇到零的话,还是先判断新开辟的数组是否越界,如果越界就停止,如果没越界拷贝在新开辟的数组中拷贝两个零,新开辟数组的指针向后移动两个单位,原来的数组向后移动一个单位。
上面讲的是异地操作,我们将异地操作优化为就地操作,首先我们考虑数组从左向右复写,首先这是不可能的,一个数组遍历一个数组进行复写的话会导致当我们复写的到零的时候后面的数已经被零覆盖了,所以遍历的时候后面会一直是零,复写就会一直复写零,所以这里我们考虑数组从后往前复写,如果从后往前复写的话我们就要考虑复写的最终位置,这里想到第一个办法就是双指针先就地模拟一遍异地操作,如果遇到零就+=2,如果遇到非零就+=1,这样用两个指针,最后一个指向末尾的元素就是就是需要拷贝的位置,另一个指针进行遍历数组并没有指向最后一个位置,所以这个位置指向的是复写的最后一个位置。通过模拟异地的复写我们已经找到最后一个复写的位置,现在只需要倒着复写即可,遇到零则复写两边,遇到非零则复写一遍。
代码展示:

class Solution
{
public:
    void duplicateZeros(vector<int>& arr) 
    {
        //先找到最后一个复写位置,模拟异地操作
        int dest=-1;
        int cur=0;
        int n=arr.size();
        while(dest<n)
        {
            if(arr[cur]!=0)
            {
                dest++;
            }
            else
            {
                dest+=2;
            }
            if(dest>=n-1)break;
            cur++;
        }
        if(dest==n)
        {
            arr[n-1]=0;
            dest-=2;
            cur-=1;
        }
        //从后向前完成复写操作
        while(cur>=0)
        {
            if(arr[cur]!=0)
            {
                arr[dest--]=arr[cur--];
            }
            else
            {
                arr[dest]=arr[cur];
                arr[dest-1]=arr[cur];
                dest-=2;
                cur-=1;
            }
        }
    }
};

3.快乐数

题目:快乐数

在这里插入图片描述

样例输入输出:

在这里插入图片描述

解法:
首先我们来看看什么事快乐数,如果各个位的平方相加,通过一系列重复操作之后,最后结果变为1,这就是快乐数,注意上面一句话很重要也就是无限循环,但可能不是1,这句话很重要。
首先我们用上面给出的两个例子来做样例:

在这里插入图片描述
上图上面样例的两个例子这样一看我们也就很熟悉了,这不是我们在链表做的链表带环的问题吗?判断链表是否带环只需要用一个快慢指针,如果两个指针,最后相遇则证明有环,如果两个指针没有相遇则证明最后没有环,这道题也很类似,这里的指针只需要抽象成操作的步骤,慢指针只操作一步,快指针操作两步,上面两种情况肯定是有环的,所以我们只需要判断两个指针相遇时的数是否是1,如果是1,则返回true,如果不是1,则返回false。

代码展示:

class Solution {
public:
    //返回n这个数每一位上的平方和
    int bitsum(int n)
    {
        int sum=0;
        while(n)
        {
            int t=n%10;
            sum+=t*t;
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow=n;//slow等于第一个数
        int fast=bitsum(n);//fast等于第二个数
        while(fast!=slow)
        {
            fast=bitsum(bitsum(fast));
            slow=bitsum(slow);
        }
        return slow==1;
    }
};

4.盛最多水的容器

题目:

在这里插入图片描述

样例输入输出:

在这里插入图片描述

题目意思很简单,这里的数组中的每个值代表的是高度,然后每个值对应的下标之差表示的是容器的宽度,宽度*高度就是我们水的最大容积。

解法一:暴力解法
暴力解法很简单,我们只需要用两个for循环每次for循环都记录一下这次的最大的容积,然后每次for循环完了之后,都对之前的容器的大小进行比较,如果新的容积大则更新容积,如果新的容积小,则不动。
解法二:双指针算法
首先我们先取首尾的指针,用下面的图讲解一下原理:
在这里插入图片描述
所以根据这个原理,向内取的话肯定是减小,所以这里我们每次肯定是小的高度进行–或者++。这里我们需要的变量就是两个首尾指针,然后还有一个记录最小值,最小值表示高度,因为高度是最小值决定的,因为向内取v是在不断减小的,所以这里我们每次更新的时候需要更新高度小的那个,更新高度大的那个会出现的情况可以看上面的图。
代码展示:

class Solution {
public:
    //时间复杂度是O(N)
    int maxArea(vector<int>& height) {
        int tail=height.size()-1;
        int head=0;
        int v=0;
        while(head<tail)
        {
            int Min=min(height[head],height[tail]);
            if(v<Min*(tail-head))
            {
                v=Min*(tail-head);
            }
            if(height[tail]<height[head])
            {
                tail--;
            }
            else
            {
                head++;
            }
        }
        return v;
    }
};

5.有效三角形的个数

题目:

在这里插入图片描述

输出输入样例:

在这里插入图片描述

解法一:暴力解法
这道题的暴力解法就是就是逐个遍历,将每一种情况遍历一遍,这道题由于不用去重,所以复杂程度直线下降,所以我们直接用三角形的判定的公式直接逐个情况判断就可以了,这里可以套三层循环,每种情况进行遍历,然后如果成立的话可以直接用一个count进行记录三角形成立的个数
解法二:双指针
步骤1:进行排序。
步骤2:排序之后,我们可以先固定最大的数,这里我们知道三角形满足a+b>c,这里c是最大的数,所以我们可以先固定最大的数,由于我们排序之后最大的数是最后一个数,所以这里我们可以直接取最后一个数为c,然后从c前面的数中取出a和b,但是a和b不是乱取的,我们先取最大的和最小的,也就是c前面的一个和第一个,如果最大的和最小的都能组成三角形,那么中间的组合肯定能组成三角形,因为中间的任何一个都比最小的大,所以这里可以直接计算三角形的个数的情况,三角形个数就是下标之差,接下来还有两种情况就是a+b==c或者a+b<c,这两种情况可以归为一种情况,就是left++,left向后移动,如果这种c的情况找完了,就可以将c–,继续找前面的情况满足的。
代码展示:

class Solution 
{
public:
    int triangleNumber(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        int count=0;
        int left=0;
        int right=nums.size()-2;
        int c=nums.size()-1;
        while(c>=2)
        {
            while(left<right)
            {
                if(nums[left]+nums[right]>nums[c])
                {
                    count+=(right-left);
                    right--;
                }
                else
                {
                    left++;
                }
            }
            //更新right和left
            c--;
            left=0;
            right=c-1;
        }
        return count;
    }
};

6.三数之和

题目:

在这里插入图片描述

样例输出和输入:

在这里插入图片描述

这里我们可以直接看样例,题目的大概意思就是我们需要从数组中找三元组,并且三元组满足三元组的每个成员都是原数组中的不同位置的数,并且三元组不能有重复,三元组还要满足一个条件就是三元组之和加起来等于0。
解法一:暴力解法
这道题的难点就是在去重上,所以这道题我们可以用三个循环,然后把每个元素都遍历一遍用一个vector存储,最后把这个vector存在vectorvector中,如果是去重的话,我们可以直接用C++的一个容器就是unordered_set进行去重,最后直接把容器返回即可。

解法二:双指针
这里双指针和上一道题的双指针类似,还是需要固定一个数,这道题我们不用unordered_set进行去重,因为在算法题中可以用,但是在面试题中用unordered_set很可能会挂掉,所以我们海狮正常的用算法进行去重,首先我们海狮先排序,排序可以把相同的元素排在一起,排完序之后先固定一个数,我们先固定首元素,然后对首元素后面的数进行遍历,这里遍历只需要取首尾元素,这道题三数之和我们就转化为了两数之和,我们只需要求后面区间的数中存在两个数加起来等于前面这个数的相反数的组合即可,如果存在,则将其入到vector中,如果不存在则继续遍历,将固定的那个数往后移动,这里我们需要注意的细节是:我们后面的数加起来有三种情况:
第一种:大于前面的数,如果大于前面的数,可以直接将最大数–,因为这个数组被我们排成有序的了,所以不可能用left++,left++只会更大,所以应该是right–。
第二种情况:left+right=-a,这个情况直接入到数组中。
第三种情况:;left+right<-a,如果小于这个数,left++,如果right–只会更小。

接下来我们来谈谈去重的操作:
在这里插入图片描述
代码展示:

class Solution 
{
public:
vector<vector<int>> v;
vector<vector<int>> threeSum(vector<int>& nums)
{
    sort(nums.begin(), nums.end());
    int i = 0;
    while (i <= nums.size() - 2)
    {
        int left = i + 1;
        int right = nums.size() - 1;
        while (left < right)
        {
            vector<int> ret;
            int ret1 = nums[left];
            int ret2 = nums[right];
            if (nums[left] + nums[right] == -nums[i])
            {
                ret.push_back(nums[left]);
                ret.push_back(nums[right]);
                ret.push_back(nums[i]);
                v.push_back(ret);
            }
            if (nums[left] + nums[right] < -nums[i])
            {
                while (nums[left] == ret1)
                {
                    left++;
                    if (left >= right)
                    {
                        break;
                    }
                }
            }
            else
            {
                while (nums[right] == ret2)
                {
                    right--;
                    if (left >= right)
                    {
                        break;
                    }
                }
            }
        }
        int j = nums[i];
        while (nums[i] == j)
        {
            i++;
            if (i > nums.size() - 1)
            {
                break;
            }
        }
    }
    return v;
}  
};

7.四数之和

题目:

在这里插入图片描述

输出样例和输入样例:

在这里插入图片描述

四数之和可以参照三数之和,题目也是类似,我们需要找到四个数的和等于目标的target,但是这四个数的位置不能是一样的,

解法一:暴力解法
暴力解法很简单,只需要用四层循环把每种情况进行遍历一遍即可,最后再用容器进行去重操作。

解法二:双指针
这里和三数取和类似,我们只需要固定第一个数,对后面的数进行三数求和,然后将第一个固定的数逐渐往后遍历即可。

代码展示:

class Solution 
{
public:
    vector<vector<int>> v;
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ret;
        //排序
        int n=nums.size();
        sort(nums.begin(),nums.end());
        for(int i=0;i<n;)
        {
            for(int j=i+1;j<n;)
            {
                int left=j+1,right=n-1;
                long long aim=(long long)target-nums[i]-nums[j];
                while(left<right)
                {
                    int sum=nums[left]+nums[right];
                    if(sum<aim)left++;
                    else if(sum>aim)right--;
                    else
                    {
                        ret.push_back({nums[left++],nums[right--],nums[i],nums[j]});
                        //nums和nums的去重
                        while(left<right&&nums[left]==nums[left-1])left++;
                        while(left<right&&nums[right]==nums[right+1])right--;
                    }
                }
                j++;
                while(j<n&&nums[j]==nums[j-1])j++;
            }
            i++;
            while(i<n&&nums[i]==nums[i-1])i++;
        }
        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/712877.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

快速压缩前端项目

背景 作为前端开发工程师难免会遇到需要把项目压缩成压缩文件来传送的情况&#xff0c;这时候需要压缩软件进行压缩文件处理 问题 项目中的依赖包文件非常庞大&#xff0c;严重影响压缩速度&#xff0c;即使想先删除再压缩&#xff0c;删除文件也不会很快完成 解决 首先要安…

Unity中实现ScrollRect 滚动定位到视口内

Demo链接: https://download.csdn.net/download/qq_41973169/89439428https://download.csdn.net/download/qq_41973169/89439428 一、前言 Unity版本:2020.1.x 如果需要资源请联系我我会分享给你 因为本人也要存储一下Demo所以上传到这里了但是又不能设置不需要积分 在Un…

零基础直接上手java跨平台桌面程序,使用javafx(六)查询sqlite数据显示到TableView中

我们使用jdbc查询sqlite的一个表显示到TableView中 在hello-view的onMouseClicked里面填上“openclick2”&#xff0c;然后在HelloController写上openclick2的相关代码FXML protected void openclick2() { }。我们要先配置好sqlite的jdbc驱动&#xff08;略&#xff09;。openc…

【34W字CISSP备考笔记】域1:安全与风险管理

1.1 理解、坚持和弘扬职业道德 1.1.1.(ISC)职业道德规范 1、行为得体、诚实、公正、负责、守法。 2、为委托人提供尽职、合格的服务。 3、促进和保护职业。 4、保护社会、公益、必需的公信和自信&#xff0c;保护基础设施。 1.1.2.组织道德规范 1、RFC 1087 &#xff0…

[大模型]XVERSE-7B-chat WebDemo 部署

XVERSE-7B-Chat为XVERSE-7B模型对齐后的版本。 XVERSE-7B 是由深圳元象科技自主研发的支持多语言的大语言模型&#xff08;Large Language Model&#xff09;&#xff0c;参数规模为 70 亿&#xff0c;主要特点如下&#xff1a; 模型结构&#xff1a;XVERSE-7B 使用主流 Deco…

HTML静态网页成品作业(HTML+CSS+JS)——游戏天天酷跑网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现图片切换轮播&#xff0c;共有4个页面。 二、…

怎么找抖音视频素材?在哪里找爆款热门的素材呢?

在短视频时代&#xff0c;拍摄和分享短视频已经成为一种潮流。但是&#xff0c;许多人都会面临一个问题&#xff0c;那就是——视频素材从哪里来&#xff1f;今天&#xff0c;我将为大家介绍几个优质的网站&#xff0c;让你的视频素材不再愁。 蛙学府&#xff1a;https://www.…

自动化测试git的使用

git是一款分布式的配置管理工具。本文主要讲git如何在自动化测试中安装&#xff0c;上传及拉取下载代码。 1 、git 介绍 每天早上到公司&#xff0c;从公司的git服务器上下载最新的代码&#xff0c;白天在最新的代码基础上&#xff0c;编写新的代码&#xff0c;下班时把“代码…

有趣网站推荐-Rainymood

听着下雨声&#xff0c;内心会平静许多&#xff0c;不知道你是否会有这种感受。 下雨声可以帮助人们放松神经&#xff0c;专注思考&#xff0c;或者只是享受一段安静的时刻&#xff0c;在繁忙的工作和生活间隙。 本期推荐网站Rain Mood 干净简洁的网站&#xff0c;只为听雨声…

C# Winform Chart图表使用和详解

Chart控件是微软自带的一种图形可视化组件&#xff0c;能展示种类丰富的图表形式。如曲线图&#xff0c;折线图&#xff0c;饼状图&#xff0c;环形图&#xff0c;柱状图&#xff0c;曲线面积图。 实例代码链接&#xff1a;https://download.csdn.net/download/lvxingzhe3/8943…

无人机RTMP推流EasyDSS直播平台推流成功,不显示直播按钮是什么原因?

互联网视频云平台/视频点播直播/视频推拉流EasyDSS支持HTTP、HLS、RTMP等播出协议&#xff0c;并且兼容多终端&#xff0c;如Windows、Android、iOS、Mac等。为了便于用户集成与二次开发&#xff0c;我们也提供了API接口供用户调用和集成。在无人机场景上&#xff0c;可以通过E…

numpy的基本操作

1.常用方法创建array print(np.array([1, 2, 3], dtype"f4"))# 32位浮点型 print(np.array([1.5, 2.2, 3]))# 默认浮点型 print(np.array([1, 2, 3, 4, 5], ndmin3))# 3维数组 print(np.array([range(i, i 5) for i in [1, 2, 3]]))# print(np.zeros(shape[5, …

如何部署 Celestia 节点:运行轻节点和全节点

最近几周&#xff0c;Celestia ($TIA) 凭借其模块化数据可用性的基本概念和突破性功能在加密社区引起了轰动。参与网络的方式多种多样&#xff0c;例如将 TIA 与验证器进行质押或在网络上构建应用程序。 用户还可以通过部署节点与区块链进行交互。本指南将解释如何设置和运行 C…

ollama 多模态llava图像识别理解模型使用

参考: https://llava-vl.github.io/ https://ollama.com/blog/vision-models https://blog.csdn.net/weixin_42357472/article/details/137666022 下载: ollama run llava:13bcli使用 图片地址前面空格就行 describe this image: /ai/a1.jpg

<Rust><iced>基于rust使用iced库构建GUI实例:图片的格式转换程序

前言 本专栏是Rust实例应用。 环境配置 平台&#xff1a;windows 软件&#xff1a;vscode 语言&#xff1a;rust 库&#xff1a;iced、iced_aw 概述 本文是专栏第二篇实例&#xff0c;是一个图像格式转换程序&#xff0c;基于rust图像处理库image以及文件处理库rfd。 UI演示&…

分数计算 初级题目

今天继续更题。今天的题目是《第五单元 分数的加减法》初级题目。 定位&#xff1a;题目较为初级&#xff0c;适合预习 参考答案&#xff1a;CACCADACAABACBBCDBCB

day54 动态规划 part10 300.最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组

300.最长递增子序列 动规五部曲 1.dp[i]的定义 dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度 2.状态转移方程 位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 1 的最大值。 所以&#xff1a;if (nums[i] > nums[j]) dp[i] max(dp[i], dp…

C++ 50 之 继承中的对象模型

继承中的对象模型 在C编译器的内部可以理解为结构体&#xff0c;子类是由父类成员叠加子类新成员而成&#xff1a; #include <iostream> #include <string> using namespace std;class Base03{ public:int m_a; protected:int m_b; private:int m_c; // 哪怕是…

【机器学习】Dify:AI智能体开发平台版本升级

一、引言 关于dify&#xff0c;之前力推过&#xff0c;大家可以跳转 AI智能体研发之路-工程篇&#xff08;二&#xff09;&#xff1a;Dify智能体开发平台一键部署了解&#xff0c;今天主要以dify为例&#xff0c;分享一下如何进行版本升级。 二、版本升级 2.1 原方案 #首次…

《2023-2024中国数据资产发展研究报告》

中国电子信息产业发展研究院发布《2023-2024中国数据资产发展研究报告》&#xff08;下称《报告》&#xff09;&#xff0c;紧跟国家战略部署&#xff0c;调研国内数据资产发展现状&#xff0c;掌握数据价值实现路径&#xff0c;助力释放数字经济新动能。 《报告》从数据资产相…