双指针详解

2023-10-31

1、定义

顾名思义,双指针即用两个不同速度不同方向的指针对数组或对象进行访问,通过两个不同指针的碰撞从而达到特定的目的。

2、解决问题

在时间或空间条件有限的情况下使用单向遍历需要消耗大量的时间或者根本无法解决问题,这时候就需要我们使用双指针,通过指针的碰撞判断是否达到条件,从而解决问题。

双指针分为快慢指针和左右指针,左右指针通常在数组有序的情况下使用,快慢指针通常在单向遍历需要消耗大量时间,或者有特定要求限制的情况下使用。

首先介绍一下左右指针

左右指针通常在数组有序的情况下,从最小和最大端同时对数组进行处理,对满足特定条件的数组元素进行成对处理,快慢指针逐渐靠拢直至发生碰撞,则遍历完所有数组。

举个例子:

一个孤岛上有7个人重量54kg,55kg,56kg,57kg,58kg,59kg,70kg。她们需要逃生到安全的地方。现在有足够的救生艇,但是每个救生艇只能坐两个人,而且每个救生艇最大能承受113kg的重量,那她们最少需要多少救生艇才能全部逃生。

现在我们来分析,如果最重的人可以与最轻的人共用一艘船,那么就这样安排。否则,最重的人无法与任何人配对,那么他们将自己独自乘一艘船。这么做的原因是,如果最轻的人可以与任何人配对,那么他们也可以与最重的人配对。

那我们首先让她们按照体重排好队
在这里插入图片描述

那我们首先看最瘦的和最胖的,连个加起来有124斤,是坐不了一条船的

在这里插入图片描述
那我们只能让最胖的自己坐一条船,然后看第二胖的能不能和最瘦的一起坐船走,这时候用了一条船。
在这里插入图片描述
可以发现最瘦的果然和第二胖的人体重一共为113kg,她们是可以一起坐船走的,这时候一共占用了两条船,接下来继续看第二瘦和第三胖的人。。。。。。。

在这里插入图片描述

最后组队情况为:

54kg - 59kg,55kg - 58kg,56kg - 57kg,70kg

从上我们可以看到双指针即是在有序数组的情况下,我们通过两个指针在遍历的过程中进行标记,对满足条件的进行处理,直至遍历完整个数组。

下面看几个例题:

881. 救生艇

第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit

返回载到每一个人所需的最小船数。(保证每个人都能被船载)。

示例 1:

输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
示例 2:

输入:[2,2,2]
输出:0
解释:不含 “山脉”。
 
提示:
0 <= A.length <= 10000
0 <= A[i] <= 10000

如上所述:如果最重的人可以与最轻的人共用一艘船,那么就这样安排。否则,最重的人无法与任何人配对,那么他们将自己独自乘一艘船。

这么做的原因是,如果最轻的人可以与任何人配对,那么他们也可以与最重的人配对。

代码如下:

class Solution {
    public int numRescueBoats(int[] people, int limit) {
        Arrays.sort(people);
        int i = 0, j = people.length - 1;
        int ans = 0;

        while (i <= j) {
            ans++;
            if (people[i] + people[j] <= limit)
                i++;
            j--;
        }

        return ans;
    }
}

快慢指针

快慢指针中的快慢即两个指针移动的快慢不同,通过两个指针移动速度的不同,判断数组或链表的长度、是否有环、特定位置的数值等。

举个例子:

假如有一条跑道,假如有环,会沿着环一直跑,乌龟每次走一步,兔子每次走两步,那么如果有环,那么他们必定会相遇,过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

环形链表问题:

141. 环形链表
142. 环形链表 II

回文链表问题:

对于回文链表问题,使用快慢指针解决,快指针步长为2,慢指针步长为1,则当快指针移动到链表末尾的时候,满指针正好移动到链表的中点。我们可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。

如下题:

234. 回文链表

回文链表问题可以分为5个步骤:

  1. 找到前半部分链表的尾节点。
  2. 反转后半部分链表。
  3. 判断是否回文。
  4. 恢复链表。
  5. 返回结果。
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) {
            return true;
        }

        // 找到前半部分链表的尾节点并反转后半部分链表
        ListNode firstHalfEnd = endOfFirstHalf(head);
        ListNode secondHalfStart = reverseList(firstHalfEnd.next);

        // 判断是否回文
        ListNode p1 = head;
        ListNode p2 = secondHalfStart;
        boolean result = true;
        while (result && p2 != null) {
            if (p1.val != p2.val) {
                result = false;
            }
            p1 = p1.next;
            p2 = p2.next;
        }        

        // 还原链表并返回结果
        firstHalfEnd.next = reverseList(secondHalfStart);
        return result;
    }

    private ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }

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

链表中倒数第K个节点问题

  1. 初始化: 前指针 former 、后指针 latter ,双指针都指向头节点 head​ 。
  2. 构建双指针距离: 前指针 former 先向前走 kk 步(结束后,双指针 former 和 latter 间相距 kk 步)。
  3. 双指针共同移动: 循环中,双指针 former 和 latter 每轮都向前走一步,直至 former 走过链表 尾节点 时跳出(跳出后, latter 与尾节点距离为 k-1k−1,即 latter 指向倒数第 kk 个节点)。
  4. 返回值: 返回 latter 即可

如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码如下:

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode former = head, latter = head;
        for(int i = 0; i < k; i++)
            former = former.next;
        while(former != null) {
            former = former.next;
            latter = latter.next;
        }
        return latter;
    }
}

寻找两个链表交点问题

160. 相交链表

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表:
在这里插入图片描述

  • 创建两个指针 pA 和 pB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
  • 当 pA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当pB 到达链表的尾部时,将它重定位到链表 A 的头结点。 若在某一时刻 pA 和 pB 相遇,则 pA/pB 为相交结点。
  • 想弄清楚为什么这样可行, 可以考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。 由于 B.length (=4) < A.length (=6),pB 比pA 少经过 22 个结点,会先到达尾部。将 pB 重定向到 A 的头结点,pA 重定向到 B 的头结点后,pB 要比pA 多走 2 个结点。因此,它们会同时到达交点。
  • 如果两个链表存在相交,它们末尾的结点必然相同。因此当 pA/pB 到达链表结尾时,记录下链表 A/B对应的元素。若最后元素不相同,则两个链表不相交。

代码如下:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /**
        定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差)
        两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度
        **/
        if(headA == null || headB == null) return null;
        ListNode pA = headA, pB = headB;
        // 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null
        while(pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

双指针详解 的相关文章

随机推荐

  • 【Error】ImportError: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29‘ not found

    参考文章 如何解决version GLIBCXX 3 4 29 not found的问题 1 问题 在 wsl ubuntu20 04 运行 yolov8 时 出现以下错误 ImportError lib x86 64 linux gnu
  • san.js源码解读之工具(util)篇——each函数

    一 迭代器模式 在开始解析源码之前 先来看一下 javascript 设计模式 迭代器模式 如果没有接触过该模式的小伙伴可能一脸疑惑 表示没听说过 但是这个迭代器模式 可能你已经用了很久了只是不知道它的名字罢了 比如 jquery中的 ea
  • 个位数统计 C语言

    1021 个位数统计 15 分 给定一个 k 位整数 N dk 1 10k 1 d1 101 d0 0 di 9 i 0 k 1 dk 1 gt 0 请编写程序统计每种不同的个位数字出现的次数 例如 给定 N 100311 则有 2 个 0
  • python萤火虫算法_萤火虫算法-python实现

    1 importnumpy as np2 from FAIndividual importFAIndividual3 importrandom4 importcopy5 importmatplotlib pyplot as plt6 7 8
  • FileNotFoundError: [Errno 2] No such file or directory: 'template/

    1 在运行generate list py时一直出现找不到templates header html和templates footer html的错误提示 2 后来才发现是路径问题 由于webapp是另外新建的目录 所以对yate py中w
  • Opencv使用cascade方法训练自己的LBP特征分类器的全过程

    前言 刚刚才把自己训练的分类器整出来 现在来理一下整个过程 从制作正负样本开始一直到最后产生自己的分类器 xml文件 因为毕设的要求 可能要用Opencv训练识别模型 用以识别道路积水 Opencv上自带的只有一些识别脸 眼睛等模型 所以要
  • 逻辑表达式三种化简方法

    逻辑表达式三种化简方法 目录 公式化简法 卡诺图化简法 机器化简法 一 公式法化简 是利用逻辑代数的基本公式 对函数进行消项 消因子 常用方法有 并项法 利用公式AB AB A 将两个与项合并为一个 消去其中的一个变量 吸收法 利用公式A
  • Unity WebGL Calls Rust Wasm

    Unity WebGL Calls Rust Wasm Jin Qing s Column May 2023 Reference https zenn dev ruccho articles 261136f7bdb003 In this a
  • 【通信原理】数字基带传输的线路码型

    数字基带传输的线路码型 简单介绍数字基带传输的线路码型的信号波形的特点 以及生成方法 注意观察频谱 文末附Matlab代码 以下包括双极性NRZ 单极型NRZ 双极型RZ 单极型RZ 差分码 曼切斯特码 数字双相码 密勒码 CMI码 AMI
  • STM32+二氧化碳传感器(FS00301)

    配置串口4 uart c u8 USART4 RX BUF USART REC LEN 接收缓冲 最大USART REC LEN个字节 u16 USART4 RX STA 0 接收状态标记 void uart4 init u32 bound
  • Istio Java SDK API - 资源访问-VirtualService/Gateway/DestinationRule/ServiceEntry

    环境 参考上一篇文章 Java如何连接Istio 参考上一篇文章 访问Isito资源 VirtualService Gateway DestinationRule ServiceEntry 项目源码 package com you micr
  • QML控件类型:Tumbler

    一 描述 Tumbler 用于通过旋转轮子来选择一个值 Tumbler model 10 API 类似于 ListView 和 PathView 等视图的 API 可以设置模型和委托 并且 count 和 currentItem 属性提供对
  • html登录页面设计

    html登录页面设计实训 html和CSS概述 1 html HTML 是一种标记语言 用于定义网页的结构和内容 包括段落 标题 列表 链接等等 它使用标签来标识不同的内容 并且这些标签可以用于嵌套 2 CSS CSS 是一种样式表语言 用
  • R语言中 attach()与detach(),及with()的使用

    attach what pos 2L name deparse substitute what backtick FALSE warn conflicts TRUE 1 attach 是对what添加路径索引 避免重复输入what名称 参数
  • 数据分析利器Python——列表、元组和字典

    文章目录 目录 文章目录 前言 一 列表和元组 1 创建列表和元组 2 列表和元组的通用用法 2 1 通过索引使用元素 2 2 子序列 2 3 加法 2 4 乘法 2 5 in运算符 2 6 长度 最大值和最小值 2 7 序列封包和序列解包
  • hdu1253 胜利大逃亡(三维bfs索搜)

    http acm hdu edu cn showproblem php pid 1253 第一次做做三维的 思路跟二维的没有区别 这道题目第一次出现Memory Limit Exceeded 这种问题 找了很长时间才发现应该是先判断在存入
  • Java8中处理日期和时间的常用API

    场景 java8中引入了一个新包java time 包含了多数会用到的核心类 注 博客 https blog csdn net badao liumang qizhi 关注公众号 霸道的程序猿 获取编程相关电子书 教程推送与免费下载 实现
  • Modbus ASCII LRC生成

    Modbus ASCII的报文生成顺序为 1 生成PDU 2 生成LRC校验码 将LRC附加到PDU后面 3 将2中的数组转换成HEX格式的文本 4 在HEX格式文本的0位置插入冒号 在HEX格式文本的后面附加Windows换行符 生成LR
  • 什么是Kubernetes?

    刚刚进学校实验室 第一次开会导师和小组同学说了n次Kubernetes 从来没听过 一脸懵逼 Kubernetes也有很多人把它叫K8S 原文链接 http omerio com 2015 12 18 learn the kubernete
  • 双指针详解

    1 定义 顾名思义 双指针即用两个不同速度或不同方向的指针对数组或对象进行访问 通过两个不同指针的碰撞从而达到特定的目的 2 解决问题 在时间或空间条件有限的情况下使用单向遍历需要消耗大量的时间或者根本无法解决问题 这时候就需要我们使用双指