2058. 找出临界点之间的最小和最大距离
文章目录
- 题目链接
- 题目描述
- 解题思路
- 题解代码
- 复杂度分析
题目链接
https://leetcode.cn/problems/find-the-minimum-and-maximum-number-of-nodes-between-critical-points/
题目描述
给定一个单链表,找出链表中所有“临界点”(critical points):某节点同时满足是极大值或极小值(严格大于/小于两侧相邻节点)。如果临界点数量少于 2,则返回 [-1, -1];否则返回:
- 最小距离:任意相邻两个临界点的节点位置差的最小值
- 最大距离:第一个临界点与最后一个临界点的位置差
解题思路
- 双指针滑动三元组:使用三个指针 a, b, c 分别指向相邻的三个节点,判断 b 是否是极大/极小点:
- 极大:a.val < b.val 且 b.val > c.val
- 极小:a.val > b.val 且 b.val < c.val
- 位置计数:用 i 表示中间节点 b 的位置(从 1 开始),当 b 为临界点时:
- 若是第一个临界点,记录 first = i
- 每次更新最后一个临界点 last = i
- 同时用 pre 记录上一个临界点的位置,以便更新最小相邻距离 minDis = min(minDis, i - pre)
- 结果返回:
- 若只有 0 或 1 个临界点(first == last),返回 [-1, -1]
- 否则返回 [最小相邻距离, last - first]
题解代码
class Solution {public int[] nodesBetweenCriticalPoints(ListNode head) {// 至少需要三个节点才可能存在临界点if (head == null || head.next == null || head.next.next == null) {return new int[]{-1, -1};}// 三元窗口 a-b-cListNode a = head, b = head.next, c = head.next.next;int first = 0; // 第一个临界点位置int last = 0; // 最后一个临界点位置(不断更新)int pre = 0; // 上一个临界点位置(用于计算相邻最小距离)int minDis = Integer.MAX_VALUE; // 相邻最小距离// i 表示 b 的位置(中间节点位置),从 1 开始for (int i = 1; c != null; i++) {// 判断 b 是否为临界点(极大或极小)if ((a.val < b.val && b.val > c.val) || (a.val > b.val && b.val < c.val)) {if (first == 0) {// 第一次遇到临界点first = i;}// 每次记录最后一个临界点last = i;// 更新相邻最小距离if (pre > 0) {minDis = Math.min(minDis, i - pre);}// 更新上一个临界点位置pre = i;}// 窗口右移a = b;b = c;c = c.next;}// 只有 0 或 1 个临界点if (first == 0 || first == last) {return new int[]{-1, -1};}// 最大距离 = 最后一个临界点 - 第一个临界点return new int[]{minDis, last - first};}
}
复杂度分析
- 时间复杂度:O(n)
- 仅一次线性扫描,每个节点最多访问常数次。
- 空间复杂度:O(1)
- 只使用常数个指针与变量。
边界与注意事项
- 链表长度小于 3 时一定不存在临界点,需要提前返回 [-1, -1]。
- 位置计数 i 与题解中一致:以中间节点 b 为基准从 1 起算,这与常见的官方解法等价(只要内部一致即可)。
- 若值有重复(如 1,1,1),不会构成严格极值,判断条件使用严格不等号是正确的。
- first 初始化为 0 用作“未赋值”的哨兵;也可用 -1,逻辑类似。