双指针问题最简单的教程(1)
什么样的问题适合用双指针技巧?当问题是从一个有序的数组或链表中,找到一个元素的子集,该子集需要满足某种限制。 这时候就特别适合用双指针。这个子集可能是某两个元素,某三个元素,甚至是一个子数组。
1 举个栗子
给你一个升序排列的数组和一个目标值,从该数组中找出两个元素,使它们的和等于目标值
看到这个问题,你会不会觉得:
这不就是两层for循环嘛。你轻松写下了如下代码:
def twoSum(nums: List[int], target: int) -> List[int]:for i in range(len(nums)):for j in range(i+1, len(nums)):if nums[i] + nums[j] == target:return [i, j]
这个解法,问题在于时间复杂度是。在实际中有点难以接受。这个时候就可以用上我们的双指针技巧。
在滑动窗口技巧中,我曾经反复强调,滑动窗口不过是对暴力搜索算法的优化。 同理,双指针技巧也是这样的。网上很多讲双指针技巧的,我还没有看到谁从这个角度来讲的,如果有麻烦加微信(milter007)告诉我。而我认为只有从这个角度来讲,才能减少认知的跳跃,更容易理解双指针的本质所在。
2 问题分析
分析上面的暴力算法,我们看到外层循环的目的,是一个一个元素看,尽量找到和该元素之和等于目标值的元素。这个过程是不可能省略的,因为省略就意味着没有查找全面彻底。
但是,内层循环是可以进行优化的。并不需要那么傻乎乎地一个一个查找。举例来说,如图所示:
Pointer1就代表上面的外层循环中的,Pointer2就代表上面的内层循环中的。只不过内层循环是从数组尾部开始而已。目标值是6。
我们看到,此时 ,于是,我们的Pointer2代表的内层循环就会往前走,如图所示:
此时,,此时,Pointer2代表的内层循环没必要继续了,因为数组是升序排列的,越往前,元素值越小,和Pointer1的和自然越小。所以,内层循环可以提前退出,这就是我们的第一个优化点。
根据上面的暴力算法,内层循环退出后,如果没有找到答案,外层循环就要继续,这就是Pointer1要向前走。Pointer1向前走一步如下所示:
我们这个例子中恰好找到答案了。但假如此时没有找到答案呢?比如这种情况:
向前移动Pointer1之后, ,这个时候,我们该怎么办呢?需要按照暴力搜索的逻辑,重启内层循环,从数组尾部开始,一个一个和此时的Pointer1(也就是2)进行匹配吗?
答案是不需要。
因为此时,我们已经可以断定,整个数组中是不可能有哪个数与2的和等于6的,为什么呢?因为 3.5 往右的每一个数,它们与1的和都是大于6的,自然与2的和更是大于6。
为什么3.5 往右的每一个数,它们与1的和都是大于6的?因为这是上一轮循环的结果,我们的Pointer2是从尾部一个一个往前推,如果与1的和大于6,就继续,直到小于6才停下来的。
而3.5往左的每一个数,都比3.5还小,它们与2的和只能比6更小。
既然这样,我们就压根不用进行任何内层循环查找了,直接将外层循环推进一步,就是将Pointer1向右推一步,这就是我们的第二个优化点。
细心的你,一定想问,如果是这种情况呢:
此时, ,那我们就需要进行内层循环了,不过,我们只用从现在的Pointer2的位置继续向数组头部方向进行查找就是,而不用从数组尾部重新开始,这就是我们的第三个优化点。
上面,其实就是双指针算法了!表面上看起来,两个指针,一会儿P2向数组头部走,一会儿P1向数组尾部走,看不出章法,第一次学习的同学,可能感到一头雾水。通过咱们的分析,原来就是对暴力查找算法的一个优化而已。 是不是顿时觉得清爽很多了?
3 代码实现
def pair_with_targetsum(arr, target_sum):left, right = 0, len(arr) - 1while(left < right):current_sum = arr[left] + arr[right]if current_sum == target_sum:return [left, right]if target_sum > current_sum:left += 1 # 移动Pointer1else:right -= 1 # 移动Pointer2
return [-1, -1]
双指针问题最简单的教程(1)相关推荐
- vim简单使用教程【转】
vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的.下面的文章翻译自<Learn Vim Progress ...
- java map set_java中Map、Set、List的简单使用教程(快速入门)
Map.Set.List List的常用方法 1.创建 List list = new ArrayList<>(); List list = new LinkedList<>( ...
- 【转】正则表达式简介及在C++11中的简单使用教程
[转]正则表达式简介及在C++11中的简单使用教程 正则表达式Regex(regular expression)是一种强大的描述字符序列的工具.在许多语言中都存在着正则表达式,C++11中也将正则表达 ...
- PHP在WPS中的应用,PHP+Laravel的简单应用教程【ajax的使用】,wps的使用教程
PHP+Laravel的简单应用教程[ajax的使用]PHP·拉弗尔的简单应用教程[阿贾克斯的使用],下面由Laravel框架教程栏目给大家介绍PHP Laravel的简单应用教程[阿贾克斯的使用], ...
- 史上最简单MySQL教程详解(进阶篇)之存储过程(一)
史上最简单MySQL教程详解(进阶篇)之存储过程(一) 史上最简单MySQL教程详解(进阶篇)之存储过程(一) 什么是存储过程 存储过程的作用 如何使用存储过程 创建存储过程 DELIMITER改变分 ...
- 史上最简单MySQL教程详解(进阶篇)之存储引擎介绍及默认引擎设置
什么是存储引擎? MySQL存储引擎种类 MyISAM 引擎 InnoDB引擎 存储引擎操作 查看存储引擎 存储引擎的变更 修改默认引擎 什么是存储引擎? 与其他数据库例如Oracle 和SQL Se ...
- IDEA简单配置教程
IDEA简单配置教程 --做好前期配置工作,后期少走弯路. 创建模块(Module) 设置(Settings) 设置主题 窗体及菜单字体及大小 设置编辑区主题 通过插件(plugins)更换主题 设置 ...
- kindle的xray怎么用_Xray简单使用教程
Xray简单使用教程 0X00下载 xray 为单文件二进制文件,无依赖,也无需安装,下载后直接使用. 下载地址为: 注意: 不要直接 clone 仓库,xray 并不开源,仓库内不含源代码,直接下载 ...
- navicat fo mysql 教程_Navicat For MySQL的简单使用教程
1.前提是必须先安装好MySQL数据库(Mac下安装MySQL数据库见前一篇) 2.安装Navicat 3.点击navicate左上角:连接->MySQL->先测链接下,如果提示连接成功, ...
最新文章
- 极限学习机的matlab程序_新程序员七宗罪,新手别踩雷
- 信息安全系统设计基础期末总结
- 刷脸背后,卷积神经网络的数学原理原来是这样的
- 串口通信模块1:串口基础知识
- 5种较为简单的缺失值处理方法
- SAP SD数据库表一览
- 基于Flink CDC打通数据实时入湖
- python调用bing翻译API V2
- opencv cvtcolor函数中断异常
- DiskPart使用方法(ZT)
- css中滑轮中是如何做的,横向的网页如何实现鼠标滑轮横向移动?_html/css_WEB-ITnose...
- WINCE下模拟鼠标点击,移动
- 秋从饶合似陶家,遍绕篱边日渐斜。不是花中偏爱菊,此花开尽更无花
- Oracle OCP(05):转换函数
- 【环境搭建】Ubuntu安装vulkan
- 3dmax入门到精通
- Py正则表达式学习笔记:特殊字符——中括号表达式、限定符表达式
- 第十四届蓝桥杯(Web 应用开发)模拟赛 1 期-职业院校组-知识点题解
- pcl启动java代码_我的世界PCL启动器-Plain Craft Launcher(PCL启动器)下载 v1.0.9免费版--pc6下载站...
- 甲骨文或收购市值775亿美元的埃森哲
热门文章
- [POJ3261] Milk Patterns
- springboot 学习笔记(三)
- 软件工程网络15结对编程作业
- react遇到的各种坑
- 如何将github上的 lib fork之后通过podfile 改变更新源到自己fork的地址
- 使用PowerShell登陆多台Windows,测试DCAgent方法
- 安卓学习-界面-ui-ListView
- 《ASP.NET MVC4 WEB编程》学习笔记------Web API 续
- EasyPHP-2.0b1+ Mantis-1.1.0安装及技巧
- 一份来自山东院校的考研调剂系统已开放名单!