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

【烧脑算法】三指针的降维打击:三线并行锁定解题细节

目录

引言

三指针

2367. 等差三元组的数目

2563. 统计公平数对的数目

795. 区间子数组个数

2444. 统计定界子数组的数目

总结


引言

三指针算法是一种基于双指针思想的扩展算法,通过引入第三个指针来解决更复杂的数组或链表问题,常用于处理需要同时维护多个区间、元素关系或特定条件的场景。其核心思想是通过三个指针的移动,高效地遍历数据结构,减少时间复杂度(通常为 O(n) 或 O(n^2)),避免暴力枚举的低效性。三指针算法相较于双指针更复杂,并且经常与其他算法进行结合,所以这里只简单分析4个三指针算法习题。

PS:本篇博客中的所有题目均来自于灵茶山艾府 - 力扣(LeetCode)分享的题单。

三指针

2367. 等差三元组的数目

题解:方法一:用set对nums中所有数据进行存储,遍历nums看set中是否存在两个数人与当前元素组成等差三元组。

class Solution {
public:int arithmeticTriplets(vector<int>& nums, int diff) {//使用map来存储nums中的所有值,遍历nums看map中是否存在可以组成等差三元组的数unordered_set<int> s;for(auto e:nums) s.insert(e);int ret=0;for(auto e:nums)if(s.count(e-diff)&&s.count(e+diff)) ret++;return ret;}
};

以上算法的时间复杂度是O(N),但是空间复杂度是O(N),能否对空间复杂度进行优化;因为数组是有序的,所以应该往双指针算法上思考。

依旧需要对nums中的数据进行枚举,设枚举到nums[i]: nums[i]-diff,nums[i],nums[i]+diff三个数据,当nums[i]在向前的时候nums[i]-diff和nums[i]+diff都是在向前的,所以可以使用两个指针来维护nums[i]-diff和nums[i]+diff。

class Solution {
public:int arithmeticTriplets(vector<int>& nums, int diff) {int n=nums.size();int left=0,right=2,ret=0;for(int i=0;i<n&&right<n;i++){//进行左右两边的查找while(nums[left]<nums[i]-diff) left++;while(right<n&&nums[right]<nums[i]+diff) right++;if(right<n&&nums[left]==nums[i]-diff&&nums[right]==nums[i]+diff) ret++;}return ret;}
};

2563. 统计公平数对的数目

题解:由题意得lower-nums[j]<=nums[i]<=upper-nums[j],当nums[i]在增大的时候,lower-nums[j]和upper-nums[j]都是在减小的,所以可以使用三指针;遍历数组,找到第一个不满足nums[left]>=lower-nums[i]的位置以及第一个满足nums[right]<=upper-nums[i]的位置,此时的(left,right]区间都是有效区间,但是在遍历数组的时候必定会出现重复数据,可以将结果/2。

class Solution {
public:long long countFairPairs(vector<int>& nums, int lower, int upper) {//lower-nums[j]<=nums[i]<=upper-nums[j]//当nums[i]在增大的时候,lower-nums[j]和upper-nums[j]都是在减小的sort(nums.begin(),nums.end());int n=nums.size();int left=n-1,right=n-1;long long ret=0;for(int i=0;i<n;i++){while(left>=0&&nums[left]>=lower-nums[i]) left--; //直到nums[left]<lower-nums[i]即nums[i]<lower-nums[left]while(right>=0&&nums[right]>upper-nums[i]) right--;  //直到nums[right]<=upper-nums[i]//此时的(leftright]是满足条件的if(left<right&&i>left&&i<=right) ret+=right-left-1; //对于i在区间内,i不可取else if(left<right) ret+=right-left;}return ret/2;}
};

对于ret的增加,也可以只取i前面的个数,这样就可以防止结果/2了。

只取i位置前面的个数,就要取min(right+1,i)-min(left+1,i) ;此次要将right+1因为当right==i的时候i是不能取的。

//此时的[left+1,right+1)是满足条件的
ret+=min(right+1,i)-min(left+1,i);

795. 区间子数组个数

题解:如上图所示,以(i0,i1]中任意一个数为左端点,i为右端点都可以组成有效区间。所以关键是找i0和i1,i0就是i前面区间中最后一个大于right的位置,i1就是i前面区间中最后一个>=left&&<=right的位置,i0和i1都是跟着i在向前面进行更新的,所以可以使用三指针进行处理。

总结:在找一个含有某数的区间范围的时候,找到最后一个不满足区间的位置和最后一个满足区间的位置就可以找到能组成有效区间的范围。

class Solution {
public:int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {//遍历数组,找最后一个>right的值,以及最后一个>=left&&<=right的值int n=nums.size(),l=-1,r=-1;int ret=0;for(int i=0;i<n;i++){if(nums[i]>=left&&nums[i]<=right) r=i;if(nums[i]>right) l=i;if(r>l) ret+=r-l;}return ret;}
};

2444. 统计定界子数组的数目

题解:从特殊到一般,与上一题思路一样,以一个位置为终点,在查找左端点;细节:子数组的最大值和最小值与minK和maxK一样,所以在找子数组的时候要注意起始位置的变化。

class Solution {
public:long long countSubarrays(vector<int>& nums, int minK, int maxK) {//使用两个数据来对时候存在相同的最大值和最小值进行标记int n=nums.size();int mini=-1,maxi=-1;long long begin=-1,ret=0;for(int i=0;i<n;i++){if(nums[i]==minK) mini=i;  //修改最近一次的最小值if(nums[i]==maxK) maxi=i;  //修改最近一次的最大值if(nums[i]<minK||nums[i]>maxK) begin=i;  //对起始位置进行修改if(begin<min(mini,maxi)) ret+=min(mini,maxi) - begin;  //更新答案}            return ret;}
};

总结

 三指针与双指针的区别在于,三指针需要对两个边界进行维护,再使用一个指针进行遍历;因此三指针的关键与双指针类似,也是要考虑对边界的移动方式和时机。

http://www.xdnf.cn/news/991837.html

相关文章:

  • 数据隐私是什么?如何做好数据隐私规范?
  • Nuttx之mm_extend
  • Python数据类型大全:整型、浮点、字符串与布尔值
  • Codeforces 1029 Div3(ABCDE)
  • Windows10下利用VS2019编译JpegLib
  • seo优化新利器:AI如何让内容批量生成与排名提升双管齐下?
  • Gremlin创建schema(包括实体和关系)
  • 【质数】埃氏筛法、线性筛法(欧拉筛法)
  • 【Linux系统编程】System V
  • Java锁机制对决:ReadWriteLock vs StampedLock
  • 从0到1落地一个RAG智能客服系统
  • ConcurrentHashMap详解:原理、实现与并发控制
  • 专访伦敦发展促进署CEO:在AI与黄仁勋之外,伦敦为何给泡泡玛特和比亚迪留了C位?
  • MySQL优化器
  • 3.3.1_2 检错编码(循环冗余校验码)
  • 【完整源码+数据集+部署教程】安检爆炸物检测系统源码和数据集:改进yolo11-REPVGGOREPA
  • 接口测试之文件上传
  • 【完整源码+数据集+部署教程】石材实例分割系统源码和数据集:改进yolo11-CA-HSFPN
  • 【Docker】快速入门与项目部署实战
  • Haclon例程1-<剃须刀片检测程序详解>
  • < 买了个麻烦 (二) 618 京东云--轻量服务器 > “可以为您申请全额退订呢。“ 工单记录:可以“全额退款“
  • linux引导过程与服务控制
  • nginx ./nginx -s reload 不生效
  • 2024-2030年中国轨道交通智能运维市场全景分析与战略前瞻
  • 永磁同步电机无速度算法--基于稳态卡尔曼滤波器SSEKF的滑模观测器
  • shell 中的 expect工具
  • AI 赋能 Java 开发:从通宵达旦到高效交付的蜕变之路
  • 如何“调优”我们自身的人体系统?
  • 以太网MDI信号PCB EMC设计要点
  • mysql 8.0引入递归cte以支持层级数据查询