双指针算法习题解答

1.移动零

题目链接:283. 移动零 - 力扣(LeetCode)

题目解析:该题要求将数组中为0的元素全部转移到数组的末尾,同时不能改变非零元素的相对位置。

解题思路:我们可以用变量dest和cur将该数组分为三个区域。如下图 

接着我们可以将数组中为0的元素放在[0,dest]的区域,将数组中非0的元素放在[dest+1,cur-1]的区域,而[cur,n-1]是待处理的区域。等数组中全部处理完之后,就如下图所示。 

实现思路:

我们用cur来遍历数组,如果cur遇到数据为0的元素,则让cur++。如果cur遇到非0的元素,因为要将非0数据放在[dest+1,cur-1]区域,所以,我们先将dest++,然后交换nums[dest]和nums[cur]的值。

代码实现

    public void moveZeroes(int[] nums) {
        int dest=-1;
        for(int cur=0;cur<nums.length;cur++){
            if(nums[cur]!=0){
                dest++;
                int tmp=nums[cur];
                nums[cur]=nums[dest];
                nums[dest]=tmp;
            }
        }
    }

2.复写零

题目链接:1089. 复写零 - 力扣(LeetCode) 

解题思路:我们可以用一个cur指针和一个dest指针,用cur指针来遍历数组并且用来确定复写的数,用dest来实现复写的操作。

首先,我们想到从前往后复写,但此时我们会发现,当cur遇到0的时候,我们用dest来实现复写的时候,由于0要复写2次,此时,会将cur后面没实现复写的一个数覆盖掉,这样就会漏掉一个数没法实现复写。

所以,我们要从后往前实现复写。

步骤1.首先,我们要找到最后一个复写的数 。

我们也可以用双指针实现找到最后一个复写的数。用cur来遍历数组,我们先让dest指向-1的位置,当cur遇到数据为非0的数,我们让dest向后走两步,如果cur遇到数据为0的数,我们让dest向后走一步,直到dest走到数组的末尾或者大于末尾的位置。

步骤2.其次我们从后向前实现复写的操作,我们也是通过cur来遍历数组,当cur遇到非0的数据,让dest向前走一步,如果cur遇到数据为0的数,我们让dest向前走两步。

但是在此之前,我们要处理一个边界情况,也就是,当我们返现当数组中最后一个要复写的数是0且该数是数组中倒数第二个数的时候,dest会越界。

如下图

最后,我们实现从后向前的复写就行了。

如下图

    public void duplicateZeros(int[] arr) {
        int cur=0;
        int dest=-1;
        int n=arr.length;
        //寻找最后一个复写的数
        while(cur<n){
            if(arr[cur]!=0){
                dest+=1;
            }else{
                dest+=2;
            }
            if(dest>=n-1){
                break;
            }
            cur++;
        }
        //处理边界情况
        if(dest>=n){
            arr[dest-1]=0;
            dest-=2;
            cur--;
        }
        //实现从后向前复写
        while(cur>=0){
            if(arr[cur]!=0){
                arr[dest--]=arr[cur];
            }else{
                arr[dest--]=0;
                arr[dest--]=0;
            }
            cur--;
        }
    }

3.快乐数

题目链接:202. 快乐数 - 力扣(LeetCode) 

解题思路:

通过题目例子,我们来手动演示以下判断该数是否为快乐数的的过程。

通过手动演示,我们发现在判断数据是否为快乐数的过程中,我们发现数据的变化会成为一个环。也就是说,在这个过程中,数据迟早会有一次演变成环中的数。

我们将上图抽象成如下图的情况

当我们抽象成右边图的时候,这就更我们在学习链表中求链表是否存在环的情况很相似。不过在这里,我们不是判断链表中是否有环,而是判断环中的数据是否为1。

前面,我们在解决判断链表中是否有环的时候,用了快慢指针,这道题,我们也可以用快慢指针。

我们每次让slow一次变化一次,让fast一次变换两次。(这里的变换是指在判断是否为快乐数的过程中,数据的变换)。

以一开始的19为例,slow变化一次就是82,fast变化两次就是68。

    public int bitSum(int n){
        int sum=0;
        while(n!=0){
            int tmp=n%10;
            sum+=tmp*tmp;
            n=n/10;
        }
        return sum;
    }
    public boolean isHappy(int n) {
        int slow=n;
        //由于要进入循环,我们一开始就要将fast放在slow后面
        int fast=bitSum(n);
        while(slow!=fast){
            slow=bitSum(slow);//让slow往后走一步
            fast=bitSum(bitSum(fast));//fast往后走两步
        }
        return slow==1;
    }

拓展:这时候,有人就会疑惑又没有可能一个数据在变换的过程中,会一直变化下去,不会成环呢?

答案就是,数据在进行变化的过程中,变化的数据一定会成环。 

证明:

这就涉及到一个鸽巢原理。

鸽巢原理:当有n+1只格子和n个鸽巢的时候,必定会有一个鸽巢的鸽子数量大于等于1,也就是总会有至少两只鸽子共享一个窝。

此时,我们将题目的数据观察题目的数据范围,如下图

n的最大值为2147483647,相同位数的最大值为9999999999,推出数据变化的范围在[1,810]之间。

所以,我们可以达到,数据在变化的过程中,变化的数肯定在[1,810]之间。

我们就算他变化无数次,变化的数据还是在[1,810]之间,所以可以得出变化的过程中,数据一定会成环。 

4.盛水最多的容器

题目链接:11. 盛最多水的容器 - 力扣(LeetCode)

解法思路:对撞指针和单调性

 我们可以设一个指针left指向数组中的第一个元素,指针right指向最后一个元素,(right-left)的值就是宽,Math(height[left],height[right])就是高,接着根据高和宽求面积。 

由于题目要求是求最大的面积,所以,我们要改变left和right的指向的位置,分别求处不同情况下的面积,然后再这些面积中求一个最大值即可。

我们以何种方式来改变left合right指向的位置呢?

我们一次只变一个,要么让left++,要么right--,我们知道面积=宽*高,而宽=right-left,而再left++的过程中或者是right--的过程中,宽度的值一定是减小的。

所以,我们让height[left]和height[right]中数值较小的值移动。

为什么呢?这就涉及到单调性。如下图解释

我们让指向值较小的指针位置变换,实质上是放弃这个较小数的枚举,因为以这个数枚举的面积都是在单调减小的,所以,我们就让数值较小的指针移动。

代码实现

    public int maxArea(int[] height) {
        int n=height.length;
        int left=0;
        int right=n-1;
        int v=0;
        while(left<right){
            int tmp=Math.min(height[left],height[right])*(right-left);
            v=Math.max(v,tmp);
            if(height[left]<height[right]){
                left++;
            }else{
                right--;
            }
        }
        return v;
    }

5.有效三角型个数

题目链接:611. 有效三角形的个数 - 力扣(LeetCode) 

解法思路:双指针和单调性

首先,我们先普及一个判断三角型的知识点,如果我们知道a<b<c,那么我们只需判断a+b>c成立,就可以判断这三条边可以组成三角形。

所以,我们可以先给数组进行排序,这是一步优化。

接着,我们就可以先固定一个最大数,因为数组排过序,是一个有序数组,那么这个最大数的左边的数是肯定都比这个最大数小的。接着建立如下图的指针位置

 

这时,我们就来判断a,b,c能否组成三角形。

如果此时a,b,c三条边能组成三角形,那么此时以9为b边,10为c边,能够成三角形的个数就为right-left 个,因为数组是一个有序的数组,那么(left,right)区间里的数都是大于a的数,那么此时就不用通过枚举b边为9的情况下,能组成三角形的情况了。

如下图

遇到这种情况,我们就让right - -就行了 

此时,会遇到第二种情况,此时nums[left]+nums[right]<nums[i],此时我们直接让left++就行了。因为此时(left,right)区间都是小于nums[right]的数了,所以我们此时就不必要以nums[left]为基准,去枚举了,此时,我们直接将此时的2去掉,及让left++就行了。

知道left和right指针相遇,我们在更换最大边,循环以上步骤。

代码实现:

    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        //固定最大数
        int ret=0,n=nums.length;
        for(int i=n-1;i>=2;i--){//固定最大边
            int left=0;
            int right=i-1;
            while(left<right){
                if(nums[left]+nums[right]>nums[i]){
                    ret+=right-left;
                    right--;
                }else{
                    left++;
                }
            }
        }
        return ret;
    }

6.和为s的两个数字 

题目链接:LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode) 

 解法一:暴力枚举法,但是当数组中的数据太多时,会超时。

    public int[] twoSum(int[] nums, int target) {
        int n=nums.length;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                if(nums[i]+nums[j]==target){
                    return new int[]{nums[i],nums[j]};
                }
            }
        }
        return new int[]{-1,-1};
    }

解法二:双指针

此时,我们注意题目中的数组是一个有序数组,所以此题我们可以利用数组的单调性和双指针进行优化。

我们分别设置一个left指针和一个right指针,先让left指向数组中的第一个数据,让right指向数组的最后一个数据,然后我们根据left和right指向值的和与target进行比较,不同的情况,让不同指针移动。如下图

此时,left和right指针在移动的过程中会遇到三种情况。

我们先假设left和right指向的值的和为sum。

第一种情况 :sum<target,如果是sum<target的情况,此时因为数组是一个有序数组,而left指向的值是数组中最小的值,right指向的值是数组中最大的值,最小值与最大值的和还是小于target的值,那么此时left指向的值与[left+1,right]区间的任何一个值相加都是小于target的,所以我们就可以大胆得放弃left指向值得枚举,即让left++。

第二种情况:sum>target,如果是sum>target的情况,最小值与最大值的和都大于target了,那么此时right指向的值与[left,right-1]区间的任何一个值相加都是大于target的,所以此时,我们可以大胆的放弃此时right指向值得枚举情况。

第三种情况:sum==target,这种情况直接返回left和right指向的值就行了。

代码实现:

    public int[] twoSum(int[] price, int target) {
        int n=price.length;
        int left=0;
        int right=n-1;
        while(left<right){
            if(price[left]+price[right]>target){
                right--;
            }else if(price[left]+price[right]<target){
                left++;
            }else{
                return new int[]{price[left],price[right]};
            }
        }
        return new int[]{-1,-1};
    }

7.三数之和  

题目链接:15. 三数之和 - 力扣(LeetCode) 

 

题目解析:求数组中三个数的和为0,并且要求三个数是数组下表不同的数,并且结果集中的一个子集不能重复出现,子集中的数据顺序也不做要求。

解法一:双指针法

求三个数的和为0,我们也可以用求两数之和的方法来解决该问题。

无非我们先固定一个数,去求另外两个数之和为固定数的相反数就行了。 求两数之和和第6题的一摸一样。

不过该题我们要去处理几个细节问题。

细节一:去重

        当left和right指针遇到相同元素之后,我们要跳过该元素。

        当i也遇到重复元素时,i也要跳过相同的元素。

细节二:不漏

        当i,left和right遇到一个符合情况的三元组时,我们继续让left++,right--。

为了方便去重,我们可以先对数组排序。

代码实现:

    public List<List<Integer>> threeSum(int[] nums) {
        int n=nums.length;
        Arrays.sort(nums);//对数组排序
        List<List<Integer>> ret=new ArrayList<>();
        for(int i=0;i<n;){
            if(nums[i]>0) break;//小优化,当nums[i]>0时,就可以跳出循环
            int target=-nums[i];
            int left=i+1;
            int right=n-1;
            while(left<right){
                int sum=nums[left]+nums[right];
                if(sum>target) right--;
                else if(sum<target) left++;
                else{
                    ret.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    left++;
                    right--;
                    while(left<right&&nums[left]==nums[left-1]) left++;//去重
                    while(left<right&&nums[right+1]==nums[right]) right--;//去重
                }
            }
            i++;
            while(i<n&&nums[i]==nums[i-1]) i++;//去重
        }
        return ret;
   }

小细节:我遇到的错误 

 

解法二:暴力枚举法

我们可以 通过排序+枚举+set去重,不过会超时。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
         List<List<Integer>> ret = new ArrayList<>();
         Set<List<Integer>> set = new HashSet<>();
         int len = nums.length;
        for(int i = 0; i < len; i++) {
            for(int j = i+1; j < len; j++) {
                for(int k = j+1; k < len; k++) {
                    if(nums[i] + nums[j] + nums[k] == 0) {
                        set.add(Arrays.asList(nums[i],nums[j],nums[k]));
                    }
                }
            }
        }
        ret.addAll(set);

        return ret;
    }
}

 8.四数之和

题目链接:18. 四数之和 - 力扣(LeetCode)

解题思路:思路和三数之和的思路差不多,不过有个例子时会超出int的最大值,所以需要我们用long来存储。

代码实现:

    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        int n=nums.length;
        List<List<Integer>> ret=new ArrayList<>();
        for(int i=0;i<n;){
            for(int j=i+1;j<n;){
                long aim=(long)target-nums[i]-nums[j];
                int left=j+1;
                int right=n-1;
                while(left<right){
                    int sum=nums[left]+nums[right];
                    if(sum>aim) right--;
                    else if(sum<aim) left++;
                    else{
                        ret.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        left++;
                        right--;
                        while(left<right&&nums[left-1]==nums[left]) left++;
                        while(left<right&&nums[right+1]==nums[right]) right--;
                    }
                }
                j++;
                while(j<n&&nums[j-1]==nums[j]) j++;
            }
            i++;
            while(i<n&&nums[i-1]==nums[i]) i++;
        }
        return ret;   
    }

 

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

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

相关文章

思源笔记轻松连接本地Ollama大语言模型,开启AI写作新体验!

文章目录 前言1. 下载运行Ollama框架2. Ollama下载大语言模型3. 思源笔记设置连接Ollama4. 测试笔记智能辅助写作5. 安装Cpolar工具6. 配置Ollama公网地址7. 笔记设置远程连接Ollama8. 固定Ollama公网地址 前言 今天我们要聊聊如何通过cpolar内网穿透技术&#xff0c;把国产笔…

SAP ABAP开发学习——WDA 五 使用表格控件实例

目录 实现 先建一个Web Dynpro Component 将两个view关联 input_view中添加按钮 output_view创建按钮 创建一个服务 input_view中使用向导创建两个输入框 output部分创建输出表单 output inbound 创建APPLICATION 效果 实现 先建一个Web Dynpro Component 将两个vi…

qt QCompleter详解

1、概述 QCompleter是Qt框架中的一个类&#xff0c;用于为文本输入提供自动完成功能。它可以与Qt的输入控件&#xff08;如QLineEdit、QTextEdit等&#xff09;结合使用&#xff0c;根据用户的输入实时过滤数据源&#xff0c;并在输入控件下方或内部显示补全建议列表。用户可以…

数据采集-Kepware连接倍福(Beckhoff)PLC(OPCUA协议)

KepserverEX 连接倍福(beckhoff)-ADS协议 系列文章目录 数据采集-Kepware 安装证书异常处理 数据采集-Kepware OPCUA 服务器实现 数据采集-Kepware连接倍福(Beckhoff)PLC(ADS协议) 目录 KepserverEX 连接倍福(beckhoff)-ADS协议系列文章目录前言一、OPC UA&#xff08;OPC统一…

vue中html如何转成pdf下载,pdf转base64,忽略某个元素渲染在pdf中,方法封装

一、下载 html2Canvas jspdf npm install jspdf html2canvas二、封装转换下载方法 htmlToPdf.js import html2Canvas from html2canvas import JsPDF from jspdf/*** param {*} reportName 下载时候的标题* param {*} isDownload 是否下载默认为下载&#xff0c;传false不…

接口测试面试题及答案(后续)

一、你们什么时候测试接口 一般有需求就会做&#xff0c;后台的接口开发好&#xff0c;就可以开始测。例外&#xff0c;如果增加了新需求&#xff0c;也要做接口测试&#xff0c;还有就是开发对后台的接口做了修改&#xff0c;交互逻辑发生变化&#xff0c;我们也要重新对接口…

萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?

随着智慧城市和数字化转型的推进&#xff0c;视频监控系统已成为保障公共安全、提升管理效率的重要工具。特别是在大中型项目中&#xff0c;跨区域的网络化视频监控需求日益增长&#xff0c;这要求视频监控管理平台不仅要具备强大的视频资源管理能力&#xff0c;还要能够适应多…

使用Qt制作一个流程变更申请流程进度以及未读消息提醒

1.1加载界面&#xff1a; 界面要素&#xff1a; 成员信息 变更位置申请 接受消息列表 根据角色加载对应界面。 1.2发起变更申请&#xff1a; 用户点击“发起变更申请”按钮。变更申请对话框可编辑&#xff0c;用户填写申请信息&#xff1a; 申请方&#xff08;自动填充&…

域名邮箱推荐:安全与稳定的邮件域名邮箱!

域名邮箱推荐及绑定攻略&#xff1f;最好用的域名邮箱服务推荐&#xff1f; 域名邮箱&#xff0c;作为一种个性化且专业的电子邮件服务&#xff0c;越来越受到企业和个人的青睐。烽火将详细介绍域名邮箱登录的全过程&#xff0c;从注册到登录&#xff0c;帮助您轻松掌握这一重…

IDEA:设置类标签栏多行显示

使用场景&#xff1a; 当我们打开的类超出一行&#xff0c;多出来的类会隐藏或者关掉&#xff0c;不利于我们开发。 解决方案&#xff1a; 1.设置多行显示 2.效果

高级图像处理工具

图像处理-高级 1、功能概览 随着社交媒体的普及和个人创作需求的增长&#xff0c;图像处理成为了日常生活中不可或缺的一部分。无论是专业的设计师还是爱好者&#xff0c;都需要一款强大的工具来帮助他们完成各种任务。今天&#xff0c;我们将介绍一款基于Python开发的高级图…

江协科技STM32学习- P38 软件SPI读写W25Q64

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

P5665 [CSP-S2019] 划分

P5665 [CSP-S2019] 划分 难度&#xff1a;省选/NOI-。 考点&#xff1a;单调队列、贪心、前缀和。 题意&#xff1a; 没有题目大意&#xff0c;本题题目描述较长&#xff0c;认真阅读每一个信息。 ​ 这个题的样例有 n n n 组数据&#xff0c;数据从 1 ∼ n 1 \sim n 1∼n…

ThreadX在STM32上的移植:F1,F4通用启动文件tx_initialize_low_level.s

在嵌入式系统开发中&#xff0c;实时操作系统&#xff08;RTOS&#xff09;的选择对于系统性能和稳定性至关重要。ThreadX是一种广泛使用的RTOS&#xff0c;它以其小巧、快速和可靠而闻名。在本文中&#xff0c;我们将探讨如何将ThreadX移植到STM32微控制器上&#xff0c;特别是…

RTT 内核基础学习

RT-Thread 内核介绍 内核是操作系统的核心&#xff0c;负责管理系统的线程、线程间通信、系统时钟、中断以及内存等。 内核位于硬件层之上&#xff0c;内核部分包括内核库、实时内核实现。 内核库是为了保证内核能够独立运行的一套小型的类似C库的函数实现子集。 这部分根据编…

qt QPixmapCache详解

1、概述 QPixmapCache是Qt框架中提供的一个功能强大的图像缓存管理工具类。它允许开发者在全局范围内缓存QPixmap对象&#xff0c;从而有效减少图像的重复加载&#xff0c;提高图像加载和显示的效率。这对于需要频繁加载和显示图像的用户界面应用来说尤为重要&#xff0c;能够…

纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码

一、纯css制作声波扩散动画 参考文章&#xff1a;纯css制作声波扩散动画 播放效果通过音频状态来控制 效果如下 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>波纹动画特效…

CocosCreator 构建透明背景应用(最新版!!!)

文章目录 透明原理补充设置截图以及代码step1: electron-js mian.jsstep2:ENABLE_TRANSPARENT_CANVASstep3:SOLID_COLOR Transparentstep:4 Build Web phonestep5:package electron-js & change body background-color 效果图补充 透明原理 使用Cocos creator 做桌面应用开…

在数据抓取的时候,短效IP比长效IP有哪些优势?

在数据抓取领域&#xff0c;代理IP的选择对于任务的成功率和效率至关重要。短效IP和长效IP各有其特点和适用场景&#xff0c;但在数据抓取过程中&#xff0c;短效IP因其独特的优势而受到青睐。本文将和大家一起探讨短效IP在数据抓取中相比长效IP的优势。 短效IP的定义与特点 …

FTP文件传输操作步骤

FTP文件传输操作步骤 步骤一&#xff1a;运行FTPServer.exe程序 步骤二、设置用户名和密码密码 步骤三、设置共享文件夹 步骤五、点击启动 步骤六、查看电脑ip(FTP server端) 步骤七、连接FTP 此电脑&#xff0c;地址栏输入&#xff1a;ftp://192.168.1.100 回车即可&…