当前位置: 首页 > news >正文

双指针-leetcode算法题总结+练习

1. 常考类型

类型常见题型示例题目(LeetCode序号)
左右夹近(Left–Right)有序求和、最值/面积、回文判断11.盛最多水的容器,15.三数之和,16.最接近的三数之和,125.验证回文串,167.两数之和 II
快慢指针(Fast–Slow)找中点、倒数第 k、判环/入口、原地删除19.删除倒数第 N 个结点,141/142.环形链表(I/II),234.回文链表,876.链表中点
滑动窗口(Sliding Window)最长/最短子串、覆盖/计数、去重3.无重复字符的最长子串,76.最小覆盖子串,209.长度最小子数组,424.替换后的最长重复字符,567.字符串的排列
同向读写指针(Read–Write)原地去重/移除、稳定保序26/80.删除有序数组中的重复项(I/II),27.移除元素,283.移动零
双序列双指针合并/对齐/区间交集21/23.合并有序链表,88.合并两个有序数组,986.区间列表的交集

 

2. 解题思路(按题型给“可直接执行”的步骤)

2.1 左右夹近(数组已排序或可排序)

二数/三数之和 & 面积类

  1. 若题目允许,先排序(可能改变原顺序要先确认题意)。

  2. l=0, r=n-1;根据目标函数选择移动方向:

    • 和太小 → l++;和太大 → r--

    • 面积/距离最值 → 每步计算答案,同时按规则收缩较差的一侧。

  3. 三数之和:外层枚举 i,内层对 l=i+1, r=n-1 夹逼,并在相等时跳重

  4. 收集/更新答案,直到 l>=r(或外层循环结束)。

简模板(Two Sum II / 有序):

int l = 0, r = nums.length - 1;
while (l < r) {int sum = nums[l] + nums[r];if (sum == target) return new int[]{l+1, r+1};if (sum < target) l++; else r--;
}

三数之和(去重!):

Arrays.sort(nums);
int n = nums.length;
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < n - 2; i++) {if (i > 0 && nums[i] == nums[i-1]) continue;int l = i + 1, r = n - 1;while (l < r) {long s = (long)nums[i] + nums[l] + nums[r];if (s == 0) {ans.add(Arrays.asList(nums[i], nums[l], nums[r]));while (l < r && nums[l] == nums[l+1]) l++;while (l < r && nums[r] == nums[r-1]) r--;l++; r--;} else if (s < 0) l++; else r--;}
}

回文串(125)

  1. l=0, r=n-1;跳过非字母数字。

  2. 比较同一大小写;相等则双向收缩;不等返回 false

  3. 走完返回 true

int l = 0, r = s.length()-1;
while (l < r) {while (l < r && !Character.isLetterOrDigit(s.charAt(l))) l++;while (l < r && !Character.isLetterOrDigit(s.charAt(r))) r--;if (Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r))) return false;l++; r--;
}
return true;

2.2 快慢指针(链表)

判环与入口(141/142)

  1. slow=head, fast=headfast 每次两步、slow 一步。

  2. 相遇则有环;无相遇到尾则无环。

  3. 找入口:p1=head, p2=相遇点 同速前进,相遇处即入口。

倒数第 k 个(19 的子步骤)

  1. fast 先走 k 步;

  2. fast/slow 同步走到 fast==nullslow 即倒数第 k。

  3. 若要删除:用哑节点 dummy,定位到 slow 的前驱再改指针。

原地删除看“下一位”(203/237)

  1. dummycur=dummy

  2. 循环条件 cur.next != null

  3. cur.next.val==val 删之,否则 cur=cur.next删除时不前进)。


2.3 滑动窗口(子串/子数组)

最长无重复子串(3)

  1. 右指针 r 扩张,把 s[r] 放入计数;

  2. 若违反要求(出现重复),移动左指针 l,边移边减少计数直到合法;

  3. 每次合法时更新答案 max = max(max, r-l+1)

int[] cnt = new int[128];
int l = 0, ans = 0;
for (int r = 0; r < s.length(); r++) {cnt[s.charAt(r)]++;while (cnt[s.charAt(r)] > 1) { cnt[s.charAt(l)]--; l++; }ans = Math.max(ans, r - l + 1);
}

最小覆盖子串(76)

  1. 统计目标串 t 的频率 need;维护 window + have/needMatch

  2. 右扩纳入字符;当 window 覆盖 need 时,尽量左缩并更新最优区间。

  3. 返回最短区间。
    (实现与你现有熟悉模板一致,这里略。)

长度最小子数组(209)

  1. sum += nums[r] 右扩;

  2. sum >= target,不断左缩并更新 ans

  3. 结束返回 ans 或 0。

int l = 0, sum = 0, ans = Integer.MAX_VALUE;
for (int r = 0; r < n; r++) {sum += nums[r];while (sum >= target) {ans = Math.min(ans, r - l + 1);sum -= nums[l++];}
}
return ans == Integer.MAX_VALUE ? 0 : ans;

2.4 同向读写指针

去重(26:保留一个)

  1. w=0 写指针;

  2. 枚举 x:若是首元素或 x != nums[w-1],写入并 w++

  3. 返回 w

int w = 0;
for (int x : nums) if (w == 0 || x != nums[w-1]) nums[w++] = x;
return w;

去重(80:最多保留两次)

int w = 0;
for (int x : nums) if (w < 2 || x != nums[w-2]) nums[w++] = x;
return w;

移除元素(27:不保序最快)

int l = 0, r = n - 1;
while (l <= r) {if (nums[l] == val) nums[l] = nums[r--];else l++;
}
return r + 1;

2.5 双序列双指针

合并两个有序数组(88,尾填避免搬运)

  1. i=m-1, j=n-1, k=m+n-1

  2. 从大到小把较大者放到 k

  3. 直到 j<0 结束。

int i=m-1, j=n-1, k=m+n-1;
while (j >= 0) {if (i >= 0 && nums1[i] > nums2[j]) nums1[k--] = nums1[i--];else nums1[k--] = nums2[j--];
}

区间交集(986)

  1. 两列表各用一指针;

  2. 取交集 [max(a.start,b.start), min(a.end,b.end)] 若合法则加入;

  3. 谁结束早,谁指针前进。

 

3. 易错点清单

  • 边界while (l < r) vs <=;三数之和外层/内层去重顺序。

  • 删除时不前进:链表用“看下一位”,否则会漏删连续目标。

  • 滑窗更新时机:最长类“合法后更新”;最短类“每次缩时更新”。

  • 排序副作用:是否允许改变原序;涉及索引时要保留原位信息。

  • 整型溢出:求和/面积用 long

  • 空/单元素:链表 head==null、数组长度 0/1 要先处理。

4. leetcode练习题

27 移除元素

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
class Solution {public int removeElement(int[] nums, int val) {int left =0;for(int i =0; i<nums.length; i++){if(nums[i] != val){nums[left] = nums[i];left++; }}return left;}
}

http://www.dtcms.com/a/333982.html

相关文章:

  • Ubuntu2204server系统安装后的初始化配置报错
  • Windows 基于ACL(访问控制列表)的权限管理
  • FTP上传文件错误
  • C# 中常用 IO 类的核心属性表格及使用示例
  • 流处理、实时分析与RAG驱动的Python ETL框架:构建智能数据管道(下)
  • FastDeploy2.0:Prometheus3.5.0通过直接采集,进行性能指标分析
  • KNN 算法详解:从电影分类到鸢尾花识别的实战指南
  • EP1C12F324I7N Altera Cyclone FPGA
  • 肖臻《区块链技术与应用》第23-26讲 - The DAO事件、BEC事件、反思和总结
  • 陪诊小程序系统开发:让就医不再是一件难事
  • UniApp 页面传参方式详解
  • 告别在线转换风险:本地运行的PDF转Word技术评测
  • Redis-plus-plus 安装指南
  • AI杀死的第一个仪式:“hello world”
  • 分享一个Oracle表空间自动扩容与清理脚本
  • 告别重复纹理:用Substance Designer构建UE5程序化地貌材质系统
  • 设计模式之静态代理
  • 基于Python3.10.6与jieba库的中文分词模型接口在Windows Server 2022上的实现与部署教程
  • 跑实验记录
  • HTTP 通信中的认证方式
  • macOS 中查看当前生效 shell 及配置文件的方法
  • Boost搜索引擎项目(详细思路版)
  • 数字化与人工智能的崛起及其社会影响研究报告
  • Navicat 为 SQLite 数据库设置密码指南
  • 学习游戏制作记录(制作系统与物品掉落系统)8.16
  • AT89C52单片机介绍
  • 《设计模式》代理模式
  • Day56 Java面向对象10 方法重写
  • 《Python学习之字典(一):基础操作与核心用法》
  • duiLib 实现鼠标拖动状态栏时,窗口跟着拖动