【优选算法】探索双指针之美(一):双指针与单调性的完美邂逅

在这里插入图片描述

文章目录

  • 前言:
    • 1.盛水最多的容器
    • 2.有效三角形个数
    • 3. 和为s的两个数字
    • 4. 三数之和
    • 5. 四数之和
  • 最后想说:

前言:

在上一章中我们已经认识到了双指针,在这章里我们就来探索一下当双指针和单调性遇见后会擦出怎样的火花呢?

我们就先来几道例题探索一下吧!

1.盛水最多的容器

题目链接: 11.盛水最多的容器
题目叙述: 给定一个长度为 n的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是(i, 0)(i, height[i])
找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。

示例 1:
在这里插入图片描述

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释: 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49


解题思路:
解法一:暴力枚举
固定最左端和右边的数依次匹配。
伪代码:

for(i = 0; i < n;i++)for(j= i + 1;Ij < n; j++)

时间复杂度:O(n^2)

解法二:利用单调性,使用双指针解决
 1. 首先定义一个left指针和一个right指针分别指向数组的最左端和最右端。
 2. 容器的体积:v = 长 * 高
长: right - left 高:left和right两者分别对应的最小的一个
  根据木桶原理
  容器的储水量v = (right - left) * min(nums[left],nums[right])
  (其中min(nums[left],nums[right])表示的是leftright两者分别对应的最小的一个.)
  举个例子:[6 , 2 ,8 , 4]
  让left指向6这个位置right指向4这个位置
v = 3 * 4 = 12v = 长 * 高,其中高不变,长减小,容器的  体积减小,所以right指向4不动,left无论指向2还是5,容器的体积总是减小,原因是高不变,长减小了(小的数向内枚举结果变小所以只需让right--再次判断,现在right指向8这个位置,v = 2 * 6 = 12right指向8左边的任何一个数容积都会减小,此时left++,以此类推。

代码实现

class Solution {
public:int maxArea(vector<int>& height) {int left = 0,right = height.size() - 1; int ret = 0;while(left < right){int v = min(height[left],height[right]) * (right - left);ret = max(v,ret);//更新指针if(height[left] < height[right]) left++;else right--;}return ret;}
};

时间复杂度:O(n)

2.有效三角形个数

题目链接:611.有效三角形的个数
题目叙述: 给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

输入: nums = [2,2,3,4]
输出: 3
解释: 有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
示例 2:

输入: nums = [4,2,3,4]
输出: 4


解题思路:
给定三个数判断能否构成三角形, 只需判断三个数中最小的两个数的和是否大于最大的数
优化:先对整个数组排序

解法一:暴力枚举
伪代码:

for(i = 0;i < n;i++)for(j = i + 1;j < n;j++)for(k = j + 1;k < n;k++)check(i,j,k);

时间复杂度:O(n^3)

解法二:利用单调性,使用双指针算法来解决
利用a + b > c
 举例数组[2,2,3,4,5,9,10]
 1. 先对整个数组进行排序
 2. 定义一个变量c固定最大的数10,定义left指向第一个元素2(a)right指向c的前一个位置9(b)a + b = 11 > c,我们会发现a以及a右边到5的数加起来总比c大,因为这个数组是有序的a + x > c,那么a加上比x大的任何数总比c大。这时共有right - left 种情况.这时只需--right.
如果a + b <= c只需++left.

 所以共会出现三种情况a + b > c,a + b < ca + b = c后两者可以归为一种情况:
   ○a + b > c ,--right
   ○a + b <= c ,++left
 2.固定下一个最大的数9,依次

代码实现:

class Solution {
public:int triangleNumber(vector<int>& nums) {//1. 优化sort(nums.begin(),nums.end());//2. 利用双指针解决问题int ret = 0 , n = nums.size();for(int i = n - 1;i >= 2;i--) //先固定最大的数{int left = 0,right = i - 1;while(left < right){if(nums[left] + nums[right] > nums[i]){ret += right - left;right--;}elseleft++;}}return ret;}
};

时间复杂度:O(n^2)

3. 和为s的两个数字

题目链接:和为s的两个数字
题目描述: 输入一个递增排序的数组和一个数字 s,在数组中查找两个数,使得它们的和正好是 s。如果有多对数字的和等于 s,则输出任意一对即可。

示例 1:
输入: nums = [2, 7, 11, 15], target = 9
输出: [2, 7] 或者 [7, 2]


解题思路:
解法一:暴力枚举
伪代码:

for(i = 0;i < n;i++)for(j = i + 1;j < n;j++)check(nums[i] + nums[j] == target);

时间复杂度:O(n^2)

解法二:利用单调性,使用双指针解决问题
 定义两个指针,left指向第一个元素,right指向最后一个元素;定义一个sumleftright指向的元素之和。
  •sum > targetright--
  •sum < targetleft++
  •sum = target返回结果
代码实现:

class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left < right) {int sum = nums[left] + nums[right];if (sum > target) right--;else if (sum < target) left++;else return {nums[left], nums[right]};}//这是为了照顾编译器,不然编译器报错:不是所有的路径都有返回值return {-1, -1};}
};

时间复杂度:O(n)

4. 三数之和

题目链接:LCR 007. 三数之和
题目描述: 给定一个包含 n 个整数的数组 nums,判断nums中是否存在三个元素 ab c ,使得 a + b + c = 0 ?请找出所有和为 0 且不重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:

输入: nums = []
输出:[]
示例 3:

输入: nums = [0]
输出:[]


解题思路:
解法一:排序+暴力枚举+利用set去重
 先对数组进行排序,然后从左向后依次枚举

时间复杂度:O(n^3)

解法二:排序+双指针
 ① 排序
 ②固定一个数a
 ③在该数后面的区间内,利用“双指针算法”快速找到两个的和为-a的即可

处理细节问题:
1.去重
 ① 找到一种结果后,leftright指针要跳过重复元素
 ②当使用完一次双指针算法后,i也需要跳过重复元素
避免越界
2.不漏
 找到一种结果后,不要“停”,缩小区间,继续寻找
代码实现:

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;//1. 排序sort(nums.begin(),nums.end());//2. 利用双指针解决问题int n = nums.size();for(int i = 0;i < n; ) // 固定数a{if(nums[i]>0) break; //小优化int left = i + 1,right = n - 1,target = -nums[i];while(left < right){int sum = nums[left] + nums[right];if(sum > target) right--;else if(sum < target) left++;else {ret.push_back({nums[i],nums[left],nums[right]});left++;right--;//去重left,rightwhile(left < right && nums[left] == nums[left-1]) left++;while(left < right && nums[right] == nums[right+1]) right--;}}//去重ii++;while(i < n && nums[i] == nums[i-1]) i++;}return ret;}
};

时间复杂度:O(n^2)

5. 四数之和

题目链接:18. 四数之和
题目描述: 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。

示例 1:

输入: nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:

输入:nums= [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]


解题思路:
解法一:排序+暴力枚举+利用set去重

解法二:排序+双指针
 ①依次固定一个数a
 ②在a后面的区间内,利用“三数之和”找到三个数,使这三个数的和为target-a即可
三数之和:固定一个数b,在b后面的区间内,利用“双指针找到两个数,使这两个数的和为target-a-b即可”

处理细节问题:
1.去重
使left,right,a,b不重
2.不漏

代码实现:

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;//1.排序sort(nums.begin(), nums.end());//2.利用双指针解决问题int n = nums.size();for (int i = 0; i < n - 1;) //先固定数 a{	//利用三数之和解决问题for (int j = i + 1; j < n;)//固定数 b{	//双指针解法int left = j + 1, right = n - 1;long long aim = (long long)target - nums[i] - nums[j];while (left < right){int sum = nums[left] + nums[right];if (sum > aim) right--;else if (sum < aim) left++;else{ret.push_back({ nums[i],nums[j],nums[left],nums[right] });left++;right--;// 去重一while (left < right && nums[left] == nums[left - 1]) left++;while (left < right && nums[right] == nums[right + 1]) right--;}}//去重二j++;while (j < n && nums[j] == nums[j - 1]) j++;}//去重三i++;while (i < n && nums[i] == nums[i - 1]) i++;}return ret;}
};

时间复杂度:O(n^3)

最后想说:

通过这篇文章你是否领略到了双指针和单调性结合后对问题的绝妙优化,面对各种问题我们都能迎刃而解,真如同“山重水复疑无路,柳暗花明又一村”啊!面对各种因暴力求解而导致的超出时间限制的问题,我们利用单调性+双指针就如同如虎添翼,把题目最优化。

如果这篇文章帮助到了你,记得评论,点赞+收藏哦,如果想要了解更多关于双指针的问题,关注博主,下篇我们就来一起探索一下由双指针构成的滑动窗口 又会有怎样的“美”呢?
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=ekjmmbfyibv0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/454943.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几何算法系列:空间实体体积计算公式推导

1.前言 面积和体积的计算是常见和基础的几何算法话题&#xff0c;面积和体积通常作为面或构件的基本信息参与相关的建模、计算、分析等过程。 有关面积的计算&#xff0c;可以参考博主此前的文章&#xff0c; 一种误差较小的轮廓面积计算算法_轮廓面积计算原理-CSDN博客文章…

深入理解Qt中的QTableView、Model与Delegate机制

文章目录 显示效果QTableViewModel(模型)Delegate(委托)ITEM控件主函数调用项目下载在Qt中,视图(View)、模型(Model)和委托(Delegate)机制是一种非常强大的架构,它们实现了MVC(模型-视图-控制器)设计模式。这种架构分离了数据存储(模型)、数据展示(视图)和数据操作(委托),使…

Apple 新品发布会亮点有哪些 | Swift 周报 issue 61

文章目录 前言新闻和社区苹果据称正洽谈投资 OpenAI 英伟达也有意跟投消息称苹果公司服务部门将裁员约百人&#xff0c;波及 Apple Books / Apple News 等业务苹果拟 9 月 10 日举行今年最重磅新品发布会&#xff0c;iPhone 16 亮相&#xff1f;都有哪些看点&#xff1f; 提案正…

京东笔试题

和谐敏感词 &#x1f517; 题目地址 &#x1f389; 模拟 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();String s scanner.next();String[] words new String[…

【Spring篇】Spring中的Bean管理

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;Spring IOC容器 &#x1f6a…

如何开启华为交换机 http

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

kafka自定义配置信息踩坑

org.apache.kafka.common.config.ConfigException: Invalid value 0 for configuration acks: Expected value to be a string, but it was a java.lang.Integer 场景描述&#xff1a; 单个kafka使用springboot框架自带的 yml 配置完全OK&#xff08;因为底层会帮我们处理好类…

QGraphics类型学习使用【Qt】【C++】

QGraphics类型学习使用 需求过程全部完整代码 首先已知&#xff0c;QGraphicsView&#xff0c;QGraphicsScene, QGraphicsItem&#xff0c;分别称为&#xff1a;视图&#xff0c;场景&#xff0c;图元&#xff0c;图表就是各种各样的元素&#xff0c;图片元素&#xff0c;线条元…

排序算法 —— 归并排序(理论+代码)

目录 1.归并排序的认识 归并排序的思想 归并排序动图演示 2.归并排序的递归实现 归并排序的遍历方式 归并排序的归并流程 归并排序的递归代码实现 3.归并排序的非递归实现 非递归实现分析 边界分析 非递归实现代码 4.归并排序总结 时间复杂度 空间复杂度 稳定性…

Postman使用-基础篇

前言 本教程将结合业界广为推崇和使用的RestAPI设计典范Github API&#xff0c;详细介绍Postman接口测试工具的使用方法和实战技巧。 在开始这个教程之前&#xff0c;先聊一下为什么接口测试在现软件行业如此重要&#xff1f; 为什么我们要学习Postman&#xff1f; 现代软件…

ubuntu 安装keepalived+haproxy

一、安装keepalived sudo apt update sudo apt install keepalived sudo systemctl start keepalived sudo systemctl enable keepalived sudo systemctl status keepalived#配置Keepalived sudo cp /etc/keepalived/keepalived.conf.sample /etc/keepalived/keepalived.conf …

Jmeter 实战 JDBC配置

​ JDBC JDBC&#xff08;Java Database Connectivity&#xff09;是一种用于执行SQL语句的Java API。通过这个API&#xff0c;可以直接连接并执行SQL脚本&#xff0c;与数据库进行交互。 使用JMeter压力测试时&#xff0c;操作数据库的场景 在使用JMeter进行接口压力测试时…

大规模多传感器滑坡检测数据集,利用landsat,哨兵2,planet,无人机图像等多种传感器采集数据共2w余副图像,mask准确标注滑坡位置

大规模多传感器滑坡检测数据集&#xff0c;利用landsat&#xff0c;哨兵2&#xff0c;planet&#xff0c;无人机图像等多种传感器采集数据共2w余副图像&#xff0c;mask准确标注滑坡位置 大规模多传感器滑坡检测数据集介绍 数据集概述 名称&#xff1a;大规模多传感器滑坡检测…

R语言建模线性回归

一、 a. # 给定的 (x, y) 数据 x <- c(2, 9, 10, 7) y <- c(3, 13, 12, 11)# 线性回归模型 y a bx model1 <- lm(y ~ x) summary(model1) # 查看回归结果# 提取系数 a 和 b a <- coef(model1)[1] b <- coef(model1)[2]# 预测值 y_pred <- predict(mode…

数据字典是什么?和数据库、数据仓库有什么关系?

一、数据字典的定义及作用 数据字典是一种对数据的定义和描述的集合&#xff0c;它包含了数据的名称、类型、长度、取值范围、业务含义、数据来源等详细信息。 数据字典的主要作用如下&#xff1a; 1. 对于数据开发者来说&#xff0c;数据字典包含了关于数据结构和内容的清晰…

【C++篇】探索STL之美:熟悉使用String类

CSDN 文章目录 前言 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&…

基于SSM+微信小程序的家庭记账本管理系统(家庭1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 1、管理员端功能有首页、个人中心、用户管理&#xff0c;消费详情管理、收入详情管理、系统管理等。 2、用户端功能有首页、消费详情、收入详情、论坛信息、我的等功能。 2、项目技术 …

python机器人编程——用python调用API控制wifi小车的实例程序

目录 一、前言二、一个客户端的简单实现2.1 首先定义一个类及属性2.2 其次定义连接方法2.3 定义一些回调函数2.4 定义发送小车指令方法2.5 定义一个正常关闭方法 三、python编程控制小车的demo实现四、小结PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源p…

【保姆级教程】DolphinScheduler本地部署与远程访问详细步骤解析

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

【牛客刷题】笔记2

目录 1、单词搜索 2、岛屿数量 2.1 DFS 2.2 BFS 3、腐烂的橘子 1、单词搜索 单词搜索_牛客题霸_牛客网 (nowcoder.com) 这道题我们就是先遍历数组board&#xff0c;若遇到了与word[0]相等的字符&#xff0c;则以这个字符为起点进行搜索&#xff0c;搜索可以是dfs&#x…