双指针算法习题解答

news2024/11/8 5:04:22

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;   
    }

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea、pycharm等软件的文件名红色怎么变绿色

1.问题 有时候在写完代码打算提交的时候&#xff0c;会遇到某个资源文件不是绿色的&#xff0c;不能提交 2.解决方法 右键该文件——git——添加即可 3.不同颜色含义 3.1 蓝色&#xff08;Blue&#xff09; 含义&#xff1a;文件被修改了但尚未保存。蓝色通常表示文件自上…

Python进阶之IO操作

文章目录 一、文件的读取二、文件内容的写入三、之操作文件夹四、StringIO与BytesIO 一、文件的读取 在python里面&#xff0c;可以使用open函数来打开文件&#xff0c;具体语法如下&#xff1a; open(filename, mode)filename&#xff1a;文件名&#xff0c;一般包括该文件所…

ECharts折线图背景渐变设置

目录 引入 1.在一个HTML文件中编写两个图表 2.渐变背景 引入 如何在一个HTML文件中编写两个图表&#xff1a;&#xff08;这个例子基于这个篇文章的基础&#xff09;一篇搞懂前端获取数据-CSDN博客 一个例子&#xff1a; 1.在一个HTML文件中编写两个图表 重点在于名字的不重…

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 二手…

CSS弹性布局:灵活布局的终极指南

在网页设计中&#xff0c;CSS 弹性布局&#xff08;Flexbox&#xff09;是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素&#xff0c;尤其是在响应式设计中表现出色。今天&#xff0c;我们就来深入探讨一下 Flexbox 的各个属性&#xff0c;让你彻底掌握这个强大的布局工…

OpenJDK Vendor下载选择

首先JDK可以通过idea进行安装 File➡️Project Structure➡️SDK➡️Add SDK➡️Download JDK 然后在JDK版本选择时&#xff0c;Idea提供了很多版本&#xff0c;让我茫然了 OpenJDK国外厂商 供应商 说明 Amazon Corretto 亚马逊云基于OpenJDK构建&#xff0c;收费 Eclipse…

SAP-ABAP开发-ONLINE 程序、DIALOG屏幕开发

目录 一、Online 程序概览 1、程序类型 2、Online程序的主要对象 二、界面 1、SAP的屏幕开发 2、屏幕功能实现 3、界面中的事件块&#xff08;Event Block&#xff09; 4、界面的创建 三、简单界面元素 1、文本/输入框控件 2、数据检查 3、一些常用的关键字 四、复…

基于vue框架的的留守儿童帮扶系统143b5(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;留守儿童,帮扶活动,申请记录,帮扶机构,帮扶进度,帮扶人,申请加入记录,参与帮扶记录 开题报告内容 基于Vue框架的留守儿童帮扶系统开题报告 一、研究背景与意义 随着城乡经济差异的不断扩大&#xff0c;大量农村劳动力涌向城市寻求更好…

xftp连接中不成功 + sudo vim 修改sshd_config不成功的解决方法

我们使用sudo vim不成功&#xff0c;但是我们使用sudo su就可以 了&#xff01; root用户权利更大&#xff01; 喵的&#xff0c;终于成功了&#xff0c;一个xftp连接半天不成功。&#xff08;添加上面的内容就可以连接成功了↑&#xff09;

这款Chrome 插件,使浏览器页面快速滑动到最底部和最顶部,并且还能...

前言 前几日我在使用谷歌浏览器&#xff0c;也就是chrome的时候&#xff0c;浏览一个内容很长的页面&#xff0c;由于页面上的内容有前后关联&#xff0c;所以我必须不停地切换到上面和下面。这非常不方便。使我非常抓狂。后来&#xff0c;我灵机一动&#xff0c;去谷歌浏览器…

嵌入式linux中设备树控制硬件的方法

大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…

2024年网鼎杯青龙组|MISC全解

转载或摘抄时请标明出处 MISC01 wdbflag{22226aba1d98c4302a6f508cad7da5d8} MISC02 一把梭工具没有任何结果&#xff0c;估计缺少符号表&#xff0c;直接strings flag > out.txt导出后慢慢找线索 在桌面上发现了png和txt文件&#xff0c;用文件名做一次筛选 第一行发现bas…

AI驱动无人驾驶:安全与效率能否兼得?

内容概要 如今&#xff0c;人工智能正以其神奇的魔力驱动着无人驾驶的浪潮&#xff0c;带来了无数令人兴奋的可能性。这一领域的最新动态显示&#xff0c;AI技术在车辆的决策过程和实时数据分析中发挥着重要作用&#xff0c;帮助车辆更聪明地应对复杂的交通环境。通过实时监测…

华为手机卸载系统应用的方法

摘要&#xff1a; 1.手机环境&#xff1a;手机需要开启开发者模式并使用usb连接电脑&#xff0c;并选择文件传输模式 2.电脑环境&#xff1a;使用鸿蒙工具箱进行傻瓜操作或安装adb工具进行命令卸载 3.鸿蒙工具箱和adb工具本质都是使用adb shell pm uninstall -k --user 0 xx…

一文看懂芯片算力TOPS

AI算力的发展与TOPS&#xff08;Tera Operations Per Second&#xff09;紧密相关。TOPS是衡量芯片每秒能执行的基本操作次数的单位&#xff0c;通常用于评估AI芯片性能&#xff0c;特别是在处理大量整数或定点运算任务时。随着AI技术进步&#xff0c;对算力的需求不断增加&…

在Linux系统中高效查找文件所在位置的方法

目录 引言 一、使用find命令查找文件 二、使用locate命令快速查找文件 三、利用which和whereis命令查找可执行文件 四、使用findmnt和mount命令查找挂载点 五、其他查找文件的方法 六、总结与最佳实践 引言 Linux操作系统以其强大的功能和灵活性而闻名&#xff0c;其文件…

连锁餐饮企业-凡塔斯,用千里聆RPA搭建用户评价管理系统,提升门店服务满意度

凡塔斯是大型连锁餐饮企业昊澜餐饮集团旗下餐饮品牌&#xff0c;是牛排自助餐头部品牌&#xff0c;旗下拥有凡塔斯、百分好、食物链KING自助烤肉及餐饮人才商学院等多个行业知名品牌。 创立至今&#xff0c;集团管理门店已发展到福建、广东、江西、浙江等十多个省市&#xff0c…

MES管理系统的生产绩效分析与资源可追踪性

在探讨MES管理系统的核心功能时&#xff0c;生产绩效分析与资源可追踪性是两个不可或缺的关键要素。它们共同构成了MES管理系统中对于生产效率、成本控制以及产品质量进行精细管理的基石。以下是对这两个关键领域的深入剖析与重新阐述。 MES管理系统中的生产绩效分析&#xff0…

远程控制项目第四天 功能实现

发送屏幕内容 代码详解 1. 创建 CImage 对象并获取屏幕内容 首先&#xff0c;我们创建一个 CImage 对象&#xff0c;用于接收屏幕上的内容。要获取屏幕内容&#xff0c;我们需要先获取当前设备上下文&#xff08;DC&#xff09;。调用 ::GetDC(NULL) 函数&#xff0c;参数 NU…

无线领夹麦克风哪个品牌音质最好?无线麦克风品牌排行榜前十名

​无线领夹麦克风哪个品牌音质最好&#xff1f;选择无线麦克风时&#xff0c;音质和耐用性至关重要。近年来&#xff0c;无线麦克风产品越来越智能化和多样化&#xff0c;但也存在着大量的劣质产品。作为测评师&#xff0c;我发现这些低质量无线麦克风由于缺乏专业调校&#xf…