【算法】2058.找出临界点之间的最小和最大距离--通俗讲解
一、题目是啥?一句话说清
给定一个链表,找出所有局部极大值点和极小值点(临界点),然后计算这些临界点之间的最小距离和最大距离。
示例:
- 输入:1 → 3 → 2 → 4 → 1 → 5
- 输出:[1, 3](最小距离1,最大距离3)
二、解题核心
遍历链表,找到所有临界点(比前后节点都大或都小的节点),记录它们的位置,然后计算相邻临界点的最小距离和首尾临界点的最大距离。
这就像在一座座山峰和山谷中,找出相邻山峰/山谷之间的最短距离和最远距离。
三、关键在哪里?(3个核心点)
想理解并解决这道题,必须抓住以下三个关键点:
1. 临界点的识别
- 是什么:局部极大值点(比前后节点都大)和局部极小值点(比前后节点都小)。
- 为什么重要:这是题目的基础,只有正确识别临界点,才能进行后续的距离计算。
2. 位置记录
- 是什么:记录每个临界点在链表中的位置(索引)。
- 为什么重要:通过位置可以计算临界点之间的距离,位置差就是距离。
3. 距离计算
- 是什么:最小距离是相邻临界点位置差的最小值,最大距离是第一个和最后一个临界点位置差。
- 为什么重要:这是题目的最终要求,需要正确计算并返回结果。
四、看图理解流程(通俗理解版本)
假设链表为:1 → 3 → 2 → 4 → 1 → 5
-
识别临界点:
- 节点3:比前后节点1和2都大 → 局部极大值点(位置1)
- 节点2:比前后节点3和4都小 → 局部极小值点(位置2)
- 节点4:比前后节点2和1都大 → 局部极大值点(位置3)
- 节点1:比前后节点4和5都小 → 局部极小值点(位置4)
-
临界点位置:[1, 2, 3, 4]
-
计算距离:
- 相邻距离:2-1=1, 3-2=1, 4-3=1
- 最小距离:min(1,1,1) = 1
- 最大距离:4-1 = 3
-
返回结果:[1, 3]
五、C++ 代码实现(附详细注释)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 链表节点定义
struct ListNode {int val;ListNode *next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode *next) : val(x), next(next) {}
};class Solution {
public:vector<int> nodesBetweenCriticalPoints(ListNode* head) {if (head == nullptr || head->next == nullptr || head->next->next == nullptr) {return {-1, -1}; // 链表长度不足3,不可能有临界点}vector<int> criticalPoints; // 存储临界点的位置int pos = 1; // 当前节点位置(从0开始计数)ListNode* prev = head;ListNode* curr = head->next;ListNode* next = curr->next;// 遍历链表,寻找临界点while (next != nullptr) {// 检查当前节点是否是临界点if ((curr->val > prev->val && curr->val > next->val) || (curr->val < prev->val && curr->val < next->val)) {criticalPoints.push_back(pos);}// 移动指针prev = curr;curr = next;next = next->next;pos++;}// 如果临界点少于2个,返回[-1, -1]if (criticalPoints.size() < 2) {return {-1, -1};}// 计算最小距离和最大距离int minDistance = INT_MAX;int maxDistance = criticalPoints.back() - criticalPoints.front();// 计算相邻临界点之间的最小距离for (int i = 1; i < criticalPoints.size(); i++) {minDistance = min(minDistance, criticalPoints[i] - criticalPoints[i - 1]);}return {minDistance, maxDistance};}
};// 辅助函数:打印数组
void printVector(const vector<int>& vec) {for (int num : vec) {cout << num << " ";}cout << endl;
}// 测试代码
int main() {// 构建示例链表:1->3->2->4->1->5ListNode* head = new ListNode(1);head->next = new ListNode(3);head->next->next = new ListNode(2);head->next->next->next = new ListNode(4);head->next->next->next->next = new ListNode(1);head->next->next->next->next->next = new ListNode(5);Solution solution;vector<int> result = solution.nodesBetweenCriticalPoints(head);printVector(result); // 输出:1 3// 释放内存while (head != nullptr) {ListNode* temp = head;head = head->next;delete temp;}return 0;
}
六、时间空间复杂度
- 时间复杂度:O(n),其中n是链表长度。需要遍历链表一次来寻找临界点,然后遍历临界点数组一次来计算距离。
- 空间复杂度:O(k),其中k是临界点的数量。用于存储临界点位置。
七、注意事项
- 链表长度检查:链表长度至少为3才可能有临界点,因为临界点需要前后节点。
- 边界条件:处理空链表、单节点链表、双节点链表等情况,这些情况都没有临界点。
- 临界点定义:严格大于或严格小于前后节点,等于的情况不算临界点。
- 位置计数:位置从0开始计数还是从1开始计数不影响结果,因为距离是相对值。
- 最小距离初始化:初始化为INT_MAX,确保能找到真正的最小值。
- 临界点数量:如果临界点少于2个,无法计算距离,直接返回[-1, -1]。