双指针的运用

news2024/9/20 0:36:07

一、双指针

双指针
常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。

1.1 对撞指针:⼀般⽤于顺序结构中,也称左右指针。

对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。
对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:
left == right (两个指针指向同⼀个位置)
left > right (两个指针错开)

1.2 快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。

这种⽅法对于处理环形链表或数组⾮常有⽤。
其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:
在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。
链表中判断是否有环
求链表中间结点的位置

二、习题

2.1 移动零

283. 移动零

        使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

        左指针左边均为非零数;

        右指针左边直到左指针处均为零。

因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变

//双指针
class Solution {

    public void moveZeroes(int[] nums) {
        int zero = -1; //处理 零
        int cur = 0; //遍历数组 处理非零
        int size = nums.length;
        for(cur = 0;cur < size;cur++){
            if(nums[cur] != 0){
                zero++;
                int tmp = nums[cur];
                nums[cur] = nums[zero];
                nums[zero] = tmp;
            }
        }
    }
}

 2.2 复写零

1089. 复写零

 

1.先确定最后一个被复写的数(cur)

通过不能超过数组长度写入元素,dest到数组末尾或者越界可以判断

2.其次处理,dest指针越界的情况

dest越界,说明dest所指位置原本应该被复写为0,越界之后这里不用复写,dest-- 回到数组末尾(size-1的位置),然后复写0。两个指针再往前移动dest--,cur--

3.从后往前,复写0

class Solution {
    public void duplicateZeros(int[] arr) {
        //1.找到最后一个“复写”的数
        int dest = -1;
        int cur = 0;
        int size = arr.length;
        while(dest < size){
            if(arr[cur] == 0){
                dest += 2;
            }else{
                dest ++;
            }
            if(dest >=size-1){
                break;
            }
            cur ++;
        }
        //2.处理边界情况
        if(dest >= size){
            // arr[size-1]=0; 这句和下一句一个意思
            arr[dest-1] = 0;
            dest -=2;
            cur--;
        }
        //3.指针从后往前复写0
        while(cur >= 0){
            if(arr[cur] ==0){
                arr[dest] = 0;
        
                dest--;
                arr[dest] = 0;
            }else{
                arr[dest] = arr[cur];
                
            }
            dest--;
            cur--;
        }
    }
}

2.3 快乐数(medium)

202. 快乐数 

题目中,关键信息:

  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。

也就是说,如果最后这个数变为1,1的平方和也是1,1会循环

(1的平方和永远也是1 :循环1-> 1->1 ->1)

                也有可能最后始终变不到 1,也会无限循环

(例如题目中的2 :2 -> 4 -> 16 -> 37 -> 58 -> 89 -> 145 -> 42 -> 20 -> 4 -> 16

无论怎样一定会形成环

为什么?

        因为n的取值为 最大为( 2,147,483,648 -1),最大数的平方和(260) 小于 自己本身,

其他数的平方和也绝对小于本身。260 -> 20 -> 4 ->16 -> 37-> 58-> 89-> 145-> 42 ->20(然后一直重复循环,永远无法为1)

        无论是否为1,他的平方和总在一定范围内变大变小,且永远不会大于自身本身。

一定会形成环

        那么提到环,就很容易想到 快慢指针,判断是否有环,因为快慢指针会在环内相遇。

根据上面描述,环要么一直是1,要么一直不是1。所以只需要判断环内任意一个数是否为1,就能判断出是否为快乐数了,而刚刚好,快慢指针相遇的点就是环内的任意一点。(很巧妙~)

//这些数会形成环 
//链表有环 - 快慢指针

class Solution {
    //求整数的每个位置上的数字的平方和
    public int function(int n){
        int sum = 0;
        while(n != 0){
            sum += Math.pow(n%10,2);
            n /= 10;
        }
        return sum;
    }
    public boolean isHappy(int n) {
        
        //1.定义快慢指针(思想)
        //不一定要使用真的指针,可以让一个数充当指针
        int slow = n;
        int fast = function(n);
        //2.找到相遇点
        while(slow != fast){
            slow = function(slow);
            fast = function(function(fast));
        }
        //3.判断相遇点的值是否为1
        return slow==1?true:false;
    }
}

2.4  盛⽔最多的容器(medium)

 

可以发现水的容量是,两个元素最小的那个值 * 两个元素之间的距离 

        类似于,数组求最大数一样,先设一个最大数max,然后比较,如果比max大就更新max,否则继续找。

        在这里,设left指向数组头,right指向数组尾,他俩先算出一个水容量最为比较的基准maxval

然后移动指针,继续比较:

       

如果此时我们固定⼀个边界,改变另⼀个边界,⽔的容积会有如下变化形式:
容器的宽度⼀定变⼩。随着指针的移动,宽度是一定会变小的。
水的高度,取决于较小的那边( 下限 )。
         如果left < right,移动left,水的高度可能增大或减小 水的容量就有机会变大
         如果移动right,水高的下限不变永远是这么小,再加上宽度减小,水容量必定减少
        由此可⻅,指针所指元素较小的指针需要移动,寻求容量变大的可能
class Solution {
    public int maxArea(int[] height) {
        int left = 0;
        int right = height.length-1;
        int maxV = 0;//保存最大容量
        int i = 0;
        while(left < right){
           int h = Math.min(height[left],height[right]); //水桶的容量取决于最短的木板
           int w = right - left;
           maxV = Math.max(maxV,h*w);//更新最大值
           i++; 
           if(height[left] < height[right]){
                left++;
           }else{
                right--;
           }
        }
        //遍历v,取最大的值
        return maxV;

    }
}

 2.5 有效三⻆形的个数(medium)

611. 有效三角形的个数

根据三角形的定义:任意两条边大于第三边

优化-> 两条最小边大于第三边

1.首先将数组从小到大排序

2.固定最长边,max指向最长边, 取值范围max >=2

3.在剩余的区间[0,max-1] 去找两边之和 大于 这条第三边

//利用单调性
//首先将数组从小到大排序
//再利用三角形的的原理:任意两条边大于第三边 优化为-> 两条最小边大于第三边
//双指针
class Solution {

    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);//排序成:小到大
        // bubblesort(nums);//排序成:小到大

        int size = nums.length;
        int sum = 0; //计数有效组合
        int max = size-1;//最长边的位置
        for(max = size-1;max >= 2;max--){
            int left = 0;//设最小边left的位置
            int right = max-1;//设最小边right的位置
            while(left < right){
                if(nums[left] + nums[right] > nums[max]){
                    sum += right-left; //组合数量
                    right--;
                }else{
                    left++;
                }
            }
        }
        return sum;
        

    }
}

2.6 和为 s 的两个数字(easy)

LCR 179. 查找总价格为目标值的两个商品

 

给出的数据,是已经排好序的,刚好可以利用这一点

双指针,一头一尾,如果和等于target目标值,返回;

                小于目标值,left++;

                大于目标值,right--;

class Solution {
    public int[] twoSum(int[] price, int target) {
        int size = price.length;
        int left = 0;
        int right = size-1;

        while(left < right){
            if(price[left] + price[right] > target){
                right--;
            }else if(price[left] + price[right] < target){
                left++;
            }else{
                break;
            }
        }
        return new int[]{price[left],price[right]};
    }
}

2.7 三数之和(medium)

可以先将数组排序,利用两数之和的特点

先固定一个数,然后再剩余的区间,求两数之和

        目标是三个数和为 0,固定一个数x之后,剩余两个数之和target就应该是-x

        如果 和大于 target,right--;

        如果 和小于 target,left++;

        等于,则加入到list中

注意点:

        1.去重(可以使用set、或者下面代码里的方法)

        2.找到之后,继续缩小区间寻找

class Solution {

    public List<List<Integer>> threeSum(int[] nums) {

        // 1.排序
        Arrays.sort(nums);

        // 2.固定一个数 nums[i]
        List<List<Integer>> ret = new ArrayList<>();
        int size = nums.length;
        int i = 0;
        // 优化2:排完序,最小和最大值乘积为正数,那比必不可能和为0
        if (nums[0] * nums[size - 1] > 0) {
            return ret;
        }
        while (i < size) {
            // 优化1:当i所指的元素已经为正数,后面的区间永远都是整数,三数之和永远不可能为0
            if (nums[i] > 0) {
                break;
            }
            int left = i + 1;
            int right = size - 1;

            int target = -nums[i];
            while (left < right) {
                // 指定的数为nums[i],区间的和应该是 -nums[i]
                int sum = nums[left] + nums[right];
                // 两者相比较
                if (sum > target) {
                    right--;
                } else if (sum < target) {
                    left++;
                } else {
                    ret.add(new ArrayList<Integer>(Arrays.asList(nums[left], nums[right], nums[i])));
                    // 继续寻找
                    left++;
                    right--;
                    // 去重
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }

                }

            }
            i++;
            while (i < size && nums[i] == nums[i - 1]) {
                // 去重,要跳过i指向的重复元素
                // 如果i下标指向的值 与 上一次指向的值相同,i直接++
                i++;
            }
        }
        return ret;
    }

}

2.8 四数之和(medium)

与三数之和类似

三数之和固定一个数,再使用双指针

四数之和,固定两个数,再使用双指针

同样需要注意,去重和继续寻找

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        // 1.排序
        Arrays.sort(nums);
        // 固定两个数
        List<List<Integer>> ret = new ArrayList<>();
        int size = nums.length;
        int left = 0;
        int right = size - 1;
        
        //固定数a
        for (int i = 0; i < size;) {
            //固定数b
            for (int j = i + 1; j < size;) {

                long target2 = (long)target - nums[i] - nums[j];
                
                left = j + 1;
                right = size - 1;
                while (left < right) {
                    if (nums[left] + nums[right] > target2) {
                        right--;
                    } else if (nums[left] + nums[right] < target2) {
                        left++;
                    } else {
                        ret.add(new ArrayList<Integer>(Arrays.asList(nums[i], nums[j], 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--;
                        }
                    }
                }
                j++;
                // 去重b
                while (j < size && nums[j] == nums[j - 1]) {
                    j++;
                }

            }
            i++;
            // 去重a
            while (i < size && nums[i] == nums[i - 1]) {
                i++;
            }
        }
        return ret;
    }
}

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

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

相关文章

一文揭秘:从零开发一套中小型医院的云HIS系统,需要多少开发成本?

从零开发一套中小型的云HIS&#xff08;医院信息系统&#xff09;系统&#xff0c;首先要组建团队&#xff0c;包括招聘、培训、设备购置&#xff0c;然后要经历需求分析、系统设计、编码、测试等多个阶段&#xff0c;这会消耗大量的时间&#xff0c;其开发成本会受到多种因素的…

《凡人歌》重塑现实主义爆款,正午阳光终于“杀”回来了

2024年的现实主义爆款&#xff0c;出自正午阳光之手。 今年剧集市场一个明显的变化在于&#xff0c;最贴近时代生活、最有“爆款潜质”的现实主义题材电视剧的缺位。根据云合数据&#xff0c;截止到2024年9月&#xff0c;年榜前十中没有一部现实主义大剧&#xff0c;古装剧却多…

在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境

目录 在线编程实现&#xff01;如何在Java后端通过DockerClient操作Docker生成python环境 一、为什么要用后端程序操作Docker 二、安装Docker 1、安装Docker 2、启动Docker 三、DockerClient与CMD操作Docker的区别 四、干货&#xff01;如何使用DockerClient实现在线编程…

通过信息架构提升商业智能:实现数据驱动创新的策略与方法

拥抱数据驱动时代 在当前的数字化转型浪潮中&#xff0c;数据已成为企业最重要的资产之一。全球范围内的网络巨头通过精细的数据管理和分析&#xff0c;已成功颠覆了传统行业。这些企业的成功不仅源于他们掌握了大量数据&#xff0c;还因为他们能有效地利用这些数据来做出精准…

研一小白读论文记录,计算机视觉,Transformer

论文是IEEE收录的一篇论文《CrossFormer: A Versatile Vision Transformer Hinging on Cross-Scale Attention》&#xff08;《基于跨尺度自注意力机制的多功能视觉Transformer》&#xff09; 泛读完之后最大的感悟有以下几点&#xff1a; 1、文章在实验的结果中呈现了大量的…

Getty Images推AI训练样本:3750张高质量照片免费开放

近日&#xff0c;全球领先的商业图库Getty Images发布了一项重大决策&#xff0c;宣布将为AI开发者提供一份包含3750张高分辨率照片的免费训练数据集。这份详尽的数据集覆盖了商业、教育、医疗健康、运动健身、物品物体、插图图标等15个不同领域&#xff0c;目的在于助力AI技术…

unity UGUI高性能飘字解决方案(对象池+合并网格)

本方案仅供参考 从需求出发 游戏类型&#xff1a;微信小游戏 帧数限定&#xff1a;60 已知的几种方案&#xff1a; 1:场上只存在一个mesh&#xff0c;每帧把所有字绘制到一个mesh。 优点&#xff1a;每帧都重绘&#xff0c;高度定制化&#xff0c;可以随意添加、删除。 …

HTTP状态码解析:在Haskell中判断响应成功与否

在互联网的世界里&#xff0c;HTTP状态码是服务器与客户端之间通信的一种语言。它们告诉我们请求是否成功&#xff0c;或者遇到了什么问题。在进行网络编程时&#xff0c;正确地解析和处理这些状态码是至关重要的。本文将探讨HTTP状态码的基本概念&#xff0c;并展示如何在Hask…

KUKA中级学习4:修改软件中机器人名字,纠正示教器时间,下载备份文件进示教器

这里写目录标题 一、修改机器人名字1.1、程序安装下载二、示教器时间修改2.1、时间修改&#xff0c;示教器全英文显示三、下载备份文件 一、修改机器人名字 1.1、程序安装下载 选下面这个 二、示教器时间修改 2.1、时间修改&#xff0c;示教器全英文显示 三、下载备份文件 …

FancyVideo环境搭建推理

引子 很少关注360开源的代码&#xff0c;最近360AI团队开源了最新视频模型FancyVideo&#xff0c;据说RTX3090可跑。可以在消费级显卡 (如 GeForce RTX 3090) 上生成任意分辨率、任意宽高比、不同风格、不同运动幅度的视频&#xff0c;其衍生模型还能够完成视频扩展、视频回溯…

springboot+vue+mybatis计算机毕业设计网上购物系统+PPT+论文+讲解+售后

本文首先实现了网上购物系统设计与实现管理技术的发展随后依照传统的软件开发流程&#xff0c;最先为系统挑选适用的言语和软件开发平台&#xff0c;依据需求分析开展控制模块制做和数据库查询构造设计&#xff0c;随后依据系统整体功能模块的设计&#xff0c;制作系统的功能模…

《JavaEE进阶》----13.<Spring Boot【配置文件】>

本篇博客讲解 1.SpringBoot配置文件的格式以及对应的语法 2.了解两个配置文件格式的差异、优缺点。 我们这里只做简单的介绍。看会&#xff0c;了解&#xff0c;学会读取就行了。 因为配置文件实在太多了&#xff0c;这里只做基础的介绍。 一、配置文件的作用 前言 计算机中有许…

E5053A 微波下变频器

_XLT新利通_ E5053A 微波下变频器 E5052B SSA 专用的微波下变频器 Keysight E5053A 是一款与 E5052B 信号源分析仪&#xff08;SSA&#xff09;相关的微波下变频器。 如果您需要设计和测试微波或毫米波频率的信号源&#xff0c;E5053A 支持您扩展该分析仪的频率范围。 从…

阿卡迈 Akamai 逆向分析2

在Lzo这个url中点击第一个 进入以后有个HPH是我们需要破解的参数 我们搜索所有的HPH 大概有10个地方&#xff0c;我们需要全部打上断点(部分HPH用不到) 大约这5个地方的HPH需要破解 第一步 清除cookie f5进行刷新 需要破解K1H&#xff0c; 58位的数组其中下标 1 3 15 25 53需…

C# winforms 窗口延迟初始化 splash 定时器

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

微课录制技巧|高效录制微课的方法,如何高效录制微课?

在教育领域&#xff0c;微课作为一种新兴的教学方式&#xff0c;越来越受到教师和学生的欢迎。本文将为您详细介绍如何高效录制微课&#xff0c;以及如何利用各种资源来提升备课和教学的质量。 微课录制技巧&#xff1a; 录制前的准备 在开始录制前&#xff0c;确保您已经明确…

客服知识库与员工培训:打造专业客服团队的秘密武器

在竞争激烈的商业环境中&#xff0c;优质的客户服务已成为企业脱颖而出的关键要素之一。而构建一个高效、专业的客服团队&#xff0c;则离不开一个全面、精准的客服知识库。客服知识库不仅是信息的宝库&#xff0c;更是员工培训与技能提升的秘密武器&#xff0c;它在新员工入职…

Adobe Illustrator非矢量图片的交集利用剪切蒙版实现

AI不支持对于非矢量图片的交集处理&#xff0c;但是可以通过剪切蒙版类似地实现需求。 如下图&#xff0c;字母F是一张PNG图片&#xff0c;为位图文件&#xff08;非矢量&#xff09;。 现在我需要将这种图片与黑色的矩形求交&#xff1a; 将两个目标全部选中&#xff0c;鼠标…

AI 浪潮中的一体化数据库|外滩大会之OceanBase实录

2024 年 9 月 5 日至 7 日&#xff0c;在上海黄浦世博园区&#xff0c;“2024 Inclusion 外滩大会”盛大举行。期间&#xff0c;9月6日&#xff0c;由OceanBase携手赛迪顾问共同策划并主办了 “AI浪潮中的分布式数据库&#xff1a;探索行业增长新动能与关键业务负载实践”。本…

优橙240419期就业榜来啦!就业班平均就业薪资8,333.3元!梦想不会发光,发光的是追梦的你!

有多坚定的信念&#xff0c;就有多勇毅的行动&#xff0c;就能开辟多光明的未来。时隔3个月&#xff0c;优橙240419就业喜报已送达&#xff01; 就业班平均就业薪资8,333.3元&#xff0c;就业学员即将奔赴祖国各地。 行百里者半九十。人类的美好理想&#xff0c;都不可能唾手而…