双指针算法:快速解决问题的小技巧(Java代码实现)

“人的一生是短暂的,但如果卑鄙地过这短暂的一生,那就太长了。”


前言

写在开始:
双指针算法是一种经典且高效的算法技巧,常用于数组、字符串等线性数据结构中的各种问题。它通过两个指针的协同移动,解决了传统暴力法需要 O(n²) 复杂度的问题,优化至 O(n)。双指针算法主要分为对撞指针和快慢指针两类,前者常用于解决有序数组和字符串的问题,后者更适合处理需要区间或步长变化的场景。掌握双指针技巧,不仅能提高解题效率,还能帮助我们更深入理解数据结构的特性与变化规律。

双指针算法其实就是通过两个“指针”来操作数据,虽然我们叫它指针,但实际上就是两个变量,
它们指的是数据中的不同位置。这个方法特别适合解决像数组、字符串这类线性结构的问题。
传统的暴力算法可能需要遍历很多次才能找到答案,而双指针通过巧妙的移动指针,
大大减少了重复操作,从而提高了效率。常见的双指针有两种,一种是从两边往中间走的对撞指针,
另一种是快慢指针,一个快一个慢,逐步缩小范围来解决问题。

在这里插入图片描述


文章有误敬请斧正 不胜感恩!

以下是本篇文章正文内容,


双指针

简介

双指针算法是一种常用的算法技巧,它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作

双指针并非真的用指针实现,一般用两个变量来表示下标 (在后面都用指针来表示)

双指针算法使用两个指针在数据结构上进行迭代,并根据问题的要求移动这些指针

双指针往往也和单调性、排序联系在一起,在数组的区间问题上,暴力法的时间复杂度往往是O(n^2)的,但双指针利用“单调性”可以优化到O(n).

常见的双指针模型有:

  1. 对撞指针
  2. 快慢指针

对撞指针

指的是两个指针 left、right (简写为l,r) 分别指l向序列第一个元素和最后一个元素

然后l指针不断递增,r不断递减,直到两个指针的值相撞或错开 (即 l >= r),或者满足其他要求的特殊条件为止。

对撞指针一般用来解决有序数组或者字符串问题 (常见于区间问题)

查找有序数组中满足某些约束条件的一组元素问题: 比如二分查找、数字之和等问题字符串反转问题:反转字符串、回文数等问题.

  1. 使用两个指针 left,right。let 指向序列第一个元素,即: left = l,right 指向序列最后一个元素,即: right = n。
  2. 在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,let ++。当满足另外定条件时,将右指针左移,right –
  3. 直到两指针相撞(即 left == right),或者满足其他要求的特殊条件时,跳出循环体

img

快慢指针

快慢指针一般比对撞指针更难想,也更难写

指的是两个指针从同一侧开始遍历序列,且移动的步长一个快一个慢

移动快的指针被称为快指针,移动慢的指针被称为慢指针

为了方便理解,我们成快指针为r,慢指针为1,这样慢指针和快指针构成区间[l,r]

两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或者满足其他特殊条件时为止

  1. 使用两个指针1、r1一般指向序列第一个元素,即: 1= 1,r一般指向序列第零个元素,即:r = 0。即初始时区间[l, r]= [1,]表示为空区间
  2. 在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即1++。当满足另外一定条件时 (也可能不需要满足条件) ,将快指针右移,即r++,保持[l,r]为合法区间
  3. 到指针移动到数组尾端 (即l== n且r== n),或者两指针相交,或者满足其他特殊条件时跳出循环体

img

例题

聪明的小羊肖恩
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int left = scan.nextInt();
        int right = scan.nextInt();
        int[] arr = new int[n];
        for(int i=0;i<n;i++){
            arr[i] = scan.nextInt();
        }
        Arrays.sort(arr);
        System.out.println(calc(arr,right)-calc(arr,left-1));
        scan.close();
    }
    static long calc(int[] arr,int t){
        long ans = 0;
        int l = 0 , r = arr.length - 1;
        while(l < r){
            while(l<r && arr[r] + arr[l] > t){
                r--;
            }
            ans += r - l;
            l++;
        }
        return ans;
    }
}
神奇的数组
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[] arr = new int[n];
        for(int i=0;i<n;i++){
            arr[i] = scan.nextInt();
        }
        int l=0,r=0,rs=0;
        long ans = 0;
        while(l<n){
            while(r<n && ((rs^arr[r]) == (rs + arr[r]))){
                rs ^= arr[r];
                r++;
            }
            ans += r - l;
            rs ^= arr[l];
            l++;
        }
        System.out.println(ans);
        scan.close();
    }
}
盛最多的水

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length - 1, res = 0;
        ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/056355574f954b2794e5e37b99b20360.jpeg#pic_center)

            res = height[i] < height[j] ?
            Math.max(res, (j - i) * height[i++]):
            Math.max(res, (j - i) * height[j--]);
        }
        return res;
    }
}

总结

双指针算法的核心在于两个指针的相互配合,通过灵活调整指针的移动策略,能够有效地解决很多复杂度较高的问题。无论是对撞指针,还是快慢指针,都是在特定场景下优化问题求解的一种利器。在面对算法问题时,我们要善于通过分析数据的单调性、区间的划分来运用双指针算法,从而提升代码的效率和简洁性。


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blaizeer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值