【Java】【python】leetcode刷题记录--双指针

news2024/11/15 23:36:03

双指针也一般称为快慢指针,主要用于处理链表和数组等线性数据结构。这种技巧主要涉及到两个指针,一个快指针(通常每次移动两步)和一个慢指针(通常每次移动一步)。快指针可以起到’探路‘的作用,给慢指针修改。
适用范围:

  1. 一般适用于字符串/数组/链表。
  2. 对数组/字符串/链表进行更改(反转、删除、增添)。

27 移除元素

题目链接
对于数组的移动、删除、插入,都可以使用双指针来处理。数组不像是链表或者以链表为底层设计的数据结构,可以从中间移除元素(例如python的list或者cpp的vector),因此如果每次都要使用暴力解法(双重for循环,每次把后面的元素进行前移),那就会导致时间复杂度为O(n^2),非常低效。而双指针(或者叫快慢指针)可以有效的利用一快一慢的特点,用快指针去”探路“,查看是否需要更改内容,而慢指针则用来配合快指针进行数组的修改,非常好用。
对于本题,我们的目标是将给定的val进行覆盖,因此可以快慢指针,快指针和慢指针开始都在同一位置,如果没有需要val,则两个指针同时前进。而如果遇到了需要覆盖的元素,就将快指针后移,直到其指向的值不为val,也就可以在下一次循环中覆盖慢指针的值,也就是val。

class Solution {
    public int removeElement(int[] nums, int val) {
       int fast=0,slow=0;
       if(nums.length == 0){
        return 0;
       }

       while(fast<nums.length){
            if(nums[fast] == val){
                fast++;
            }else{
                nums[slow] = nums[fast];
                slow++;
                fast++;
            }
       }
       return slow;
    }
}

344.反转字符串

题目链接
本题就是双指针比较简单的用法,一头一尾进行交换即可。左右指针不断缩紧直到左指针大于等于右指针为主。

class Solution {
    public void reverseString(char[] s) {
        int left=0,right=s.length-1;

        while(left<right){
            char tmp = s[left];
            s[left++] = s[right];
            s[right--] = tmp;
        }
    }
}

151 反转字符串中的单词

题目链接
在java里,字符串是不可变的,即无法修改。使用split方法可以将字符串转化为字符串数组。用trim和split一起处理字符串的格式(防止错误的空格),然后只需要对字符串数组进行双指针的操作即可。

res是经过处理的字符串数组,例如s是”hello world",则res[0]是hello,res[1]为world。

class Solution {
    public String reverseWords(String s) {
        String res[] = s.trim().split("\\s+");

        int slow=0,fast=res.length-1;
        while(slow<fast){
            String tmp=res[fast];
            res[fast--] = res[slow];
            res[slow++] = tmp;

        }

        return String.join(" ",res);
    }
}

上述的内容都是针对数组,而双指针在链表里也及其常用。

206 反转链表

题目链接
如果单独定义一个链表会导致空间的浪费,而如果暴力做会导致时间复杂度为O(n^2),因此可以定义一个空指针,并且将整个链表倒转。比如我们一开始可以理解是这样:
在这里插入图片描述
可以申请一个空指针prev,在1的左边,让1的指针指向prev,也就是只改变指针的方向即可。代码如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        ListNode temp = null;

        while(cur!=null){
            temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = temp;
        }

        return prev;       

    }
}

19. 删除链表的倒数第 N 个结点

题目链接
如果不止一趟遍历来做这个题也是可以的,但是很麻烦,这里可以用快慢指针来做:先让快指针走n次,再让快慢指针同时走,最后去掉slow后的结点即可。
但对于链表的题目,我们最好准备一个虚拟头节点,因为会涉及到很多删除头节点的情况。比如我们的链表只有1个节点,或者两个节点但是要删除头节点,加入特判语句会让代码不那么优雅。
同时返回的时候,也不能返回head,如果只有一个节点,那么我们返回的就是被删除的节点了(或者两个节点但是要删除头节点也是一样的)。代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode slow = dummy;
        ListNode fast = dummy;
        
        for(int i=0; i<n; i++){
            fast = fast.next;
        }

        while(fast.next!=null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;

        return dummy.next;
    }
}

面试题 02.07. 链表相交

题目链接
这题只是稍稍麻烦了点,但是思路还是很简单的。先进行a和b的遍历,计算出a和b链表的长度,再让两个指针在同一起跑线上,即是双指针的方法。例如a的长度更长,那么就可以让a先走(cnt_a - cnt_b)步,这样指针就在同一起跑线,然后进行节点的对比即可。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int cnt_a=0,cnt_b=0;
        ListNode a = headA;
        ListNode b = headB;

        // a或者b为空

        //
        while(a!=null){
            a = a.next;
            cnt_a++;
        }
        while(b!=null){
            b = b.next;
            cnt_b++;
        }
        // 对齐
        a = headA;
        b = headB;
        if (cnt_a > cnt_b) {
            for (int i = 0; i < cnt_a - cnt_b; i++) {
                a = a.next;
            }
        } else if (cnt_a < cnt_b) {
            for (int i = 0; i < cnt_b - cnt_a; i++) {
                b = b.next;
            }
        }
        // 找交点
        while(a!=null&&b!=null){
            if(a == b){
                return a;
            }else{
                a = a.next;
                b = b.next;
            }
        }
        return null;

    }
}

142 环形链表 II

题目链接
顺带一提,如果判断链表是否成环,也可以用双指针进行判断,代码如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }

        ListNode slow = head;
        ListNode fast = head.next;

        while (fast != null && fast.next != null) {
            if (slow == fast) {
                return true; // 快指针和慢指针相遇,说明存在环
            }
            slow = slow.next; // 慢指针每次移动一步
            fast = fast.next.next; // 快指针每次移动两步
        }

        return false; // 快指针到达链表末尾,说明不存在环
    }
}

对于本题,需要动笔画一画:

在这里插入图片描述
我们将整个链表分为x y z三部分,而快指针和慢指针一开始都在head。slow每次走一步,而fast每次是两步。这样一定会有一个相遇点p(图中的y下面的点)。对走过的路径分析,slow走过了x+y,而fast是x+y+n*(y+z),而快指针走的长度是slow的二倍(可以自己试一试),因此可以计算得到x的值。
观察得到的表达式,也就是如果有一个新指针再从head开始出发,每次走一步,而slow从p点出发,那么两指针交点则为环入口。因此可以写代码如下:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        while(fast!=null&&fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow==fast){
                ListNode res = head;
                while(slow!=res){
                    slow = slow.next;
                    res = res.next;
                }
                return res;
            }
        }
        return null;

    }
}

15 三数之和

题目链接

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

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

相关文章

2-EMMC启动及各分区文件生成过程

EMMC的使用比nand flash还是复杂一些&#xff0c;有其特有的分区和电器性能 1、启动过程介绍 跟普通nand或spi flash不同&#xff0c;uboot前面还有好几级 在vendor某些厂商的设计中&#xff0c;ATF并不是BOOTROM加载后的第一个启动镜像&#xff0c;可能是这样的&#xff1a; …

WPF/C#:理解与实现WPF中的MVVM模式

MVVM模式的介绍 MVVM&#xff08;Model-View-ViewModel&#xff09;是一种设计模式&#xff0c;特别适用于WPF&#xff08;Windows Presentation Foundation&#xff09;等XAML-based的应用程序开发。MVVM模式主要包含三个部分&#xff1a;Model&#xff08;模型&#xff09;、…

启智CV机器人,ROS,ubuntu 20.04 【最后一步有问题】

资料&#xff1a; https://wiki.ros.org/kinetic/Installation/Ubuntu https://blog.csdn.net/qq_44339029/article/details/120579608 装VM。 装ubuntu20.04 desktop.iso系统。 装vm工具&#xff1a; sudo apt update sudo dpkg --configure -a sudo apt-get autoremove o…

一些关于深度聚类以及部分对比学习的论文阅读笔记

目录 资料SwAV问题方法方法的创新点为什么有效有什么可以借鉴的地方聚类Multi-crop 代码 PCL代码 Feature Alignment and Uniformity for Test Time Adaptation代码 SimSiam 资料 深度聚类算法研究综述(很赞&#xff0c;从聚类方法和深度学习方法两个方面进行了总结&#xff0…

windows ollama 指定模型下载路径

为Ollama指定模型的下载路径 在Windows系统中&#xff0c;如果想为Ollama指定模型的下载路径&#xff0c;可以通过设置环境变量来实现。以下是详细的步骤&#xff1a; 确定默认下载路径&#xff1a; 默认情况下&#xff0c;Ollama的模型可能会下载到C:\Users\<用户名>…

使用Spring Boot编写的小项目

加法计算器 前端代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…

JavaScript基础(十)

上一篇学了各种数组方法&#xff0c;正好先做个练习回忆一下: 排序并去重 我随便写一组数&#xff0c;要求排好并去掉重复的: var arr [2,8,1,7,2,6,1,5,2,7,6,5]; for (var i0; i<arr.length; i){ for (var ji1; j<arr.length; j){ if(arr[i]arr[j]){ arr.splice(j,1)…

前端路由 Hash 模式和 History 模式

在SPA单页面模式盛行&#xff0c;前后端分离的背景下&#xff0c;我们要弄清楚路由到底是个什么玩意&#xff0c;它可以帮助我们加深对于前端项目线上运作的理解。 而现在我们常见的路由实现方式&#xff0c;主要有两种&#xff0c;分别是history和hash模式。 理解 如何理解路…

配餐中的红酒温度控制与口感体验

在红酒配餐中&#xff0c;温度控制是影响口感体验的重要因素之一。合适的温度可以释放红酒的香气和风味&#xff0c;使酒体更加圆润和丰富。云仓酒庄雷盛红酒以其卓着的品质和与众不同的口感&#xff0c;成为了红酒爱好者们的首要选择品牌。下面将介绍如何通过温度控制提升红酒…

奈飞CEO最新访谈:抢走你饭碗的不是AI,而是能熟练使用AI的人

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【windows】Total Uninstall:一款功能强大的完全卸载软件

软件介绍 Total Uninstall是一款专业的软件卸载工具&#xff0c;旨在帮助用户彻底地清除计算机上的应用程序&#xff0c;包括与应用程序相关的所有文件和注册表项。以下是Total Uninstall的一些主要功能和特点&#xff1a; 完全卸载&#xff1a;软件可以监视应用程序的安装过程…

nodejs版本管理切换工具nvm介绍、nvm下载、nvm安装、配置及nvm使用

最近很多同学问&#xff0c;在工作中&#xff0c;同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff…

TypeScript-类型断言

类型断言 当开发者比TS本身更清楚当前的类型是什么&#xff0c;可以使用断言(as)让类型更加精确和具体 const _link document.getElementById(link) console.log(_link.href) // 出错了&#xff0c;如下图 const _link document.getElementById(link) as HTMLAnchorElement…

JVM的相关知识

目录 JVM内存划分 类加载过程 类加载中的“双亲委派模型” JVM内存划分 JVM也就是java进程。这个进程一旦跑起来之后&#xff0c;就会从操作系统里&#xff0c;申请一大块内存空间。JVM接下来就要进一步的对这个大的空间进行划分。划分成不同区域&#xff0c;从而每个区域都…

惯性测量单元M-G370系列广泛用于工业系统各个领域

爱普生现已推出型号为M-G370系列的高稳定性、高精度及极小尺寸封装的惯性测量单元(IMU)&#xff0c;可广泛应用于工业系统的各个领域。 为了节省PCB的面积和产品空间&#xff0c;M-G370系列性测量单元设计精巧&#xff0c;且具有6个自由度:三轴角速率和三轴线性加速度&…

如何使用git上传linux下的项目!---附带每一步截图

在实际项目中&#xff0c;我们需要把自己的模块递给GitHub&#xff0c;需要别人的模块的时候拉下来&#xff0c;那么我们怎么把自己的项目递给GitHub呢&#xff1f;下面做一个总结&#xff1a; 登录GitHub 创建一个仓库 填写相关信息 项目名称是必填的&#xff0c;项目描述可以…

RK3568平台(camera篇)V4L2查询获取设置设备

一.查询设备能力VIDIOC_QUERYCAP struct v4l2_capability cap; ioctl(fd, VIDIOC_QUERYCAP, &cap) struct v4l2_capability 结构体描述了视频采集设备的 driver 信息。 struct v4l2_capability { __u8 driver[16]; // 驱动名字 __u8 card[32]; // 设备名字 __u8 bus_inf…

CSS学习笔记:Less

什么是Less&#xff1f; Less是一个CSS预处理器&#xff0c; Less文件后缀是.less 扩充了CSS 语言&#xff0c;使CSS具备一定的逻辑性、计算能力 可以通俗地理解&#xff1a;Less是一种更好用的CSS 注释 运算 嵌套 Less嵌套的作用&#xff1a;快速生成后代选择器 变量 问…

【Spring MVC】_SpringMVC项目返回数据

目录 1. 注解使用示例 1.1 使用Controller注解 1.2 使用RestController注解 1.3 使用Controller与ResponseBody注解 2. 关于ResponseBody注解 前文已经介绍过使用Controller注解向前端返回一个HTML页面&#xff0c;接下来将介绍向前端返回数据。 关于Controller和RestCon…

Rohm公司参展欧洲PCI盛会

​德国历史悠久的文化名城纽伦堡&#xff0c;即将迎来一场科技盛宴——欧洲PCI展览会。在这个为期三天的盛会中&#xff08;6月11日至13日&#xff09;&#xff0c;Rohm公司将以璀璨之姿&#xff0c;特别聚焦宽带隙&#xff08;WBG&#xff09;设备的璀璨光芒。 此次&#xff0…