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

算法-双指针技巧

文章目录

    • 算法概述
    • 奇偶数字归位
    • 寻找重复数字
    • 接雨水
    • 救生艇问题

算法概述

设置两个指针的技巧,其实这种说法很宽泛,似乎没什么可总结的

  1. 有时候所谓的双指针技巧,就单纯是代码过程用双指针的形式表达出来而已。
    没有单调性(贪心)方面的考虑
  2. 有时候的双指针技巧包含单调性(贪心)方面的考虑,牵扯到可能性的取舍。

对分析能力的要求会变高。其实是先有的思考和优化,然后代码变成了双指针的形式。3)所以,双指针这个“皮”不重要,分析题目单调性(贪心)方面的特征,这个能力才重要。
常见的双指针类型:
3. 同向双指针
4. 快慢双指针
5. 从两头往中间的双指针
6. 其他

奇偶数字归位

leetcode922.按奇偶排序数组2
在这里插入图片描述
题目分析 :
设置两个指针, 一个指针指向奇数位置, 一个指向偶数位置, 然后看数组的最后一个位置进行发货, 如果是奇数发往奇数位置, 奇数位指针前进两步, 如果是偶数发往偶数位置, 偶数指针前进两步, 如果有一个指针越界了就说明数组已经排好了, 因为数组中奇数跟偶数的个数是一致的, 代码实现如下

class Solution {//简单的双指针的解法, 给出来一个奇数位的指针, 偶数位的指针public int[] sortArrayByParityII(int[] nums) {//简单的发货的逻辑(只盯着最后一个位置看)for(int i = 1, j = 0; i < nums.length && j < nums.length; ){//如果最后一个数是奇数就跟奇数位交换, 奇数指针+2, 偶数同理if((nums[nums.length - 1] & 1) == 1){swap(nums, i, nums.length - 1);i += 2;}else{swap(nums, j, nums.length - 1);j += 2;}}return nums;}//交换的方法private void swap(int[] nums, int i, int j){int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}

寻找重复数字

leetcode287.寻找重复数
在这里插入图片描述
题目分析:
这道题其实是链表里面的快慢指针寻找入环节点的题是一样的(不是很好理解), 看自己的理解了, 我们只提供一下代码思路, 寻找入环节点的步骤就是快指针一次走两步, 慢指针一次走一步, 然后快慢指针相遇, 重置一个指针为0位置, 然后快慢指针一次都走一步, 最后相遇的位置就是第一个入环的节点

class Solution {//快慢指针法求解第一个入环节点(快指针走两步, 慢指针走两步)public int findDuplicate(int[] nums) {int slow = nums[0];int fast = nums[nums[0]];//找到第一个相遇的位置while(fast != slow){fast = nums[nums[fast]];slow = nums[slow];}//重置其中一个指针为起始位置, 进行同速率移动slow = 0;while(slow != fast){slow = nums[slow];fast = nums[fast];}return fast;}
}

接雨水

leetcode42.接雨水
在这里插入图片描述
题目分析(初始版本) :
这个题就是典型的逐渐优化代码从而转化为一个双指针的形式的例子, 我们的核心是想要找到一个位置可以存储多少的水量, 可以得知, 这个其实是由左右两侧的最大值的最小值决定的
在这里插入图片描述
如图所示, 三条黑色柱子从左到右代表着待求位置的左侧最大值, 待求位置, 和待求位置的右侧的最大值, 所以我们只要找到最大值中的较小的那个然后减去待求的高度就是该位置存储的水量, 事实真的是这样吗 ? 其实不然, 因为如果最大值中的最小值小于中间的值该位置就不会存储水…
核心公式 :

water = Math.max(0, Math.min(lmax, rmax) - height)

所以我们需要对每一个位置的左右两侧的最值进行查询, 所以我们需要生成两个预处理的结构, 代码实现如下

class Solution {//双指针(对撞双指针)解决接雨水问题(一维的接雨水)public int trap(int[] height) {//基础的版本(运用辅助空间)int len = height.length;//开始生成预处理结构int[] LMHeight = new int[len];LMHeight[0] = height[0];for(int i = 1; i < len; i++){LMHeight[i] = Math.max(height[i], LMHeight[i - 1]);}int[] RMHeight = new int[len];RMHeight[len - 1] = height[len - 1];for(int i = len - 2; i >= 0; i--){RMHeight[i] = Math.max(RMHeight[i + 1], height[i]);}//下面的求解的过程就是找到一个位置可以存储几格水int ans = 0;for(int i = 1; i < len - 1; i++){ans += Math.max(0, Math.min(RMHeight[i + 1], LMHeight[i - 1]) - height[i]);}return ans;}
}

双指针版本的优化就是
我们定义两个指针, 每次我们都去结算最小的那一侧, 如下图所示
在这里插入图片描述
对于左侧l指针指向的数据, 左侧的最大值是确定的, 但是右侧的最大值是不确定的(但是肯定大于等于当前的右侧最大值), 而对于右侧r指针指向的数据, 右侧的最大值是确定的, 但是左侧的最大值是不确定的(但是肯定大于等于当前的左侧最大值), 所以此时结算右侧r指针的数据, 因为r指针的数据瓶颈就是右侧的rmax…分析完毕, 左侧同理, 代码实现如下

class Solution {//双指针(对撞的指针)解决双指针的问题public int trap(int[] height) {//下面是优化过后的接雨水的解法 --> 时间复杂度O(n), 空间复杂度O(1)int ans = 0;//创建的两个指针int left = 1;int right = height.length - 2;//左右侧的最大值(对于左指针来说, 左侧的最大值是真实的, 右侧的是虚拟的, 右侧指针同理)int lmax = height[0];int rmax = height[height.length - 1];while(left <= right){if(lmax < rmax){//此时结算左侧ans += Math.max(0, lmax - height[left]);lmax = Math.max(lmax, height[left++]);}else {//此时结算右侧ans += Math.max(0, rmax - height[right]);rmax = Math.max(rmax, height[right--]);}}return ans;}
}

救生艇问题

leetcode881.救生艇
在这里插入图片描述
题目分析 :
这道题就有一点贪心的感觉在里面了, 给定两个指针l, r 一个指向左侧位置一个指向右侧位置, 然后如果 peo[l] + peo[r] <= limit, l++, r–, 如果peo[r] > limit, r–; 这样就可以确保每一步都不会亏, 最后返回的船数就是最好的结果

class Solution {//比较经典的双指针(对撞双指针)加上贪心的应用public int numRescueBoats(int[] people, int limit) {//首先进行一部排序(这个也是决定时间复杂度的一步, O(n * logn))Arrays.sort(people);//返回的答案int ans = 0;for(int l = 0, r = people.length - 1; l <= r; ){int sum = l == r ? people[l] : people[l] + people[r];if(sum > limit){//此时最大的自己单独一个船r--;}else{//此时这一轮的最大值跟最小值一个船r--;l++;}ans++;}return ans;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 搭建Kafka+zookeeper集群调度
  • 运营如何判断账号是否起号失败?
  • Bev pool 加速(1): torch.autograd.Function的使用
  • 从C到C++
  • 微信小程序-文件下载
  • 体系结构权衡分析方法(ATAM)
  • 基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
  • 键盘快捷键:提高工作效率与电脑操作的利器
  • IIS 反向代理模块: URL Rewrite 和 Application Request Routing (ARR)
  • SparkSQL SET和RESET
  • Spring boot启动过程详解
  • 形象化理解pytorch中的tensor.scatter操作
  • VsCode 内置 Git 可视化操作【初始化仓库】
  • HarmonyOS NEXT 底部选项卡功能
  • Excel排序错误原因之一
  • 《深入 React 技术栈》
  • 【技术性】Search知识
  • 【前端学习】-粗谈选择器
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • flask接收请求并推入栈
  • httpie使用详解
  • HTTP中的ETag在移动客户端的应用
  • Java超时控制的实现
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Lucene解析 - 基本概念
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • Redis在Web项目中的应用与实践
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • sublime配置文件
  • 程序员该如何有效的找工作?
  • 前端技术周刊 2019-02-11 Serverless
  • 数据仓库的几种建模方法
  • 思维导图—你不知道的JavaScript中卷
  • 我有几个粽子,和一个故事
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​一些不规范的GTID使用场景
  • #数学建模# 线性规划问题的Matlab求解
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • $jQuery 重写Alert样式方法
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (附源码)php新闻发布平台 毕业设计 141646
  • (十八)Flink CEP 详解
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .Net IE10 _doPostBack 未定义
  • .py文件应该怎样打开?
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @RequestBody与@ResponseBody的使用
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [240727] Qt Creator 14 发布 | AMD 推迟 Ryzen 9000芯片发布
  • [BZOJ] 3262: 陌上花开
  • [C#基础]说说lock到底锁谁?
  • [C++]高精度 bign (重载运算符版本)
  • [C++]类和对象【下】