LeetCode 热题 100 题解(二):双指针部分(1)

题目一:移动零(No. 283)

题目链接:https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=top-100-liked


给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]

示例 2:

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

提示:

  • 1 <= nums.length <= 104
  • 231 <= nums[i] <= 231 - 1

题解

本题要求是将所有的 0 移动到数组的末尾,同时要保证原本的顺序,直接顺着这个思路去实现是很困难的,此时可以换一个思路:将所有非零的数字都移到最前面就可以了,这样就可以很容易的保证移动之后的顺序,具体可以看下面的动画演示。

在这里插入图片描述

图片来源:王尼玛

首先声明两个指针,一个指针去遍历数组查找 非零 的数据,另一个指针去标明替换的位置,当指针 a 遍历到非零的元素,就与 b 指针进行替换的操作。

所以此时的思路就是两个指针分别去遍历,当 b 寻找到为 0 的节点就停住,等待 a 找到非零的节点进行替换的操作,写出代码是这样的:

class Solution {public void moveZeroes(int[] nums) {int j = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] != 0 && nums[j] == 0) {// 如果找到了非零的节点就进行替换的操作int temp = nums[i];nums[i] = nums[j];nums[j] = temp;j++;}if (nums[j] != 0) j++; // 如果不是 0,继续向后寻找}}
}

但其实不管 j 的位置是否为 0 其实都可以进行替换的操作,这里先给出代码:

class Solution {public void moveZeroes(int[] nums) {int j = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] != 0) { // 找到了非零的节点int temp = nums[i];nums[i] = nums[j];nums[j] = temp;j++;}}}
}

这样的写法其实就是融合了上面的两种情况,如果 nums[j] != 0 的时候也执行替换的操作

  • nums[j] != 0nums[i] != 0 的情况下执行替换的操作的情况,只有一种可能 就是i == j 的情况,此时执行的就是自身和自身替换,然后同时后移一位

我们在 if 语句中加一个语句来验证这个结论:if (nums[j] != 0) System.*out*.println(i == j);
测试用例:1, 3, 4, 0, 0, 1, 0, 3, 12,此时输出的为 true true true

我们来多测试几组试一下:1, 4, 0, 0, 1, 0, 3, 12,此时输出的是 true true

那 1, 0, 0, 1, 0, 3, 12 呢?猜一下也能知道是 1

这些是执行自身替换的次数,这种行为会在 遇到第一个0的时候 停止,在遇到 0 之后,j 就会跟不上 i 的速度,**从而一定会留在为 0 的位置上,**这个大家举几个案例就可以很轻松的看出来,j 一定会留在为 0 的位置上;相较于第一种方法,第二种简化了判断的逻辑,执行速度比第一种快得多。

为了少执行替换操作,可以在方法上执行这个逻辑

  			int temp = 0;for(int i = 0; i < nums.length; i++) {if (nums[i] == 0) {temp = i;break;}} int j = temp;

先找到为零的节点再执行下面的代码,但其实这样优化的意义不是特别大,但对理解这个解法比较有帮助。

题目二:盛水最多的容器(No. 11)

题目链接:https://leetcode.cn/problems/container-with-most-water/description/?envType=study-plan-v2&envId=top-100-liked


给定一个长度为 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。

示例 2:

输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

题解

为了求容器的容量,本题需要考虑到两个因素,分别是两个容器壁的最低高度,第二个是两个容器壁之间的距离,这两个限制条件很难去同时兼顾到,所以本题首先想到的方式就是穷举法,将所有可能出现的情况都列举出来,一定不要忽视穷举法,很多好的方法都是在穷举的基础上优化来的。

穷举的思路就是指定两个指针,left 和 right,left 首先指向 0,right 指针指向数组的末尾,然后对 right 进行递减的操作,知道 right == left 结束此次循环,然后执行 left++,再次从末尾去循环 right。

在遍历中不断去取得容量,直到遍历完成后输出最终的容量。

此时,思考一个问题,如果我发现 height[left] < height[right] ,还有必要去遍历整个数组嘛?

如果 height[left] < height[right] 那此时的容量就是 height[left] * (right - left) ,此时我去移动 right,那只有两种情况:

高度比刚刚的还高或者高度比刚刚的还低,无论是哪种情况,得到的结果 一定是小于最初的情况的;所以得出一个结论,如果我发现 height[left] < height[right] 就直接结束本次遍历即可

class Solution {public int maxArea(int[] height) {int res = 0;for (int left = 0; left < height.length - 1; left++) {int right = height.length - 1;while (right > left) { // 循环遍历 rightint temp = (right - left) * Math.min(height[left], height[right]);res = Math.max(temp, res);if (height[left] < height[right]) break;right--;} }return res;}
}

写出代码就是这样子的。

但是上面的解法时间复杂度仍然非常高,left 要遍历完整个数组,那有没有可能提前结束呢?

反过来去思考这道题目,其实我固定 right,然后去从头遍历 left 也是可以的,终止条件就变为了 height[right] < height[left]

那此时就有一个新的思路,两个指针分别指向开头和结尾,如果我发现左边的比较矮,我就移动左指针,反之则移动右指针,这样就同时运用到了固定 left 和固定 right 两种情况的限制条件,从而进一步的优化解法:

class Solution {public int maxArea(int[] height) {int left = 0;int right = height.length - 1;int res = 0;while (left < right) {int h = Math.min(height[left], height[right]);int temp = (right - left) * h;res = Math.max(temp, res);if (height[left] <= height[right]) {// 如果左边的低left++;} else {// 如果右边的低right--;}}return res;}
}

题目三:三数之和(No. 15)

题目链接:https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked


给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • 105 <= nums[i] <= 105

题解

本题中给出一个数组,求数组中和为 0 的三元组,且要求 不能重复。

之前题解中我们讲解过 两数之和 这道题目,本题如果固定一个元素,然后本题就转化为在剩余的元素中,找两个数字,其和为 0 - 固定的数字 。这样就把三树之和转化为了两数之和来求解,这是求解本题的大致思路。

但是与两数之和不同的是,本题中要求了 去重

为了将相同的元素聚集在一起,就可以执行排序的方法,通过 Arrays.sort() 方法排序,可以将所有相同的元素聚集在一起,方便后续进行去重的操作。

首先来考虑第一个元素的选取,经过排序之后,数组是这样的:

在这里插入图片描述

当选定一个元素后,剩下两个元素的查找范围就是下一个一直到末尾(为了不出现重复的情况),在这种情况下选定的这个元素就 **一定是负数,**因为如果选定的是正数的话,在查找范围内无法形成和为 0 的情况。

那此时第一个元素的范围就确定了:

for (int i = 0; i < nums.length; i++) {if (nums[i] > 0) {return res;}if (i > 0 && nums[i] == nums[i - 1]) {continue;}// 其他的逻辑
}

上面代码展示的就是遍历第一个元素的逻辑,首先要保证 i 不会越界,其次要保证 nums[i] 是小于等于零的,当发现 nums[i] > 0 的时候就说明本次的查找已经结束了,直接将 res 返回;第二段 if 进行去重的,就是如果发现 nums[i] == nums[i - 1] (和上一个相同),此时就直接遍历下一个元素。

然后就是在剩余的部分中找到两个数,使得它们三个的和为 0,当然可以使用前面的哈希,但这里因为经过了排序,我们使用一个新的双指针方法来解决:

首先指定两个指针,来规范查找的范围:

int left = i + 1;
int right = nums.length - 1;

此时的和为: nums[left] + nums[right] + nums[i] ,然后去判断这个和是大于、小于还是等于 0。

  • 如果发现大于零,就说明取得值太大了,此时左移 right 指针
  • 如果发现小于零,则说明取得值太小了,此时右移 left 指针
  • 如果恰好等于零就将其存储下来
      while(left < right) {int temp = nums[left] + nums[right] + nums[i];if (temp > 0) {right--;} else if (temp < 0) {left++;} else {res.add(Arrays.asList(nums[i], nums[left], nums[right])); // 去重逻辑right--;left++;}}

在恰好等于的情况需要考虑去重的逻辑,否则就会出现重复的情况,此时就需要移动指针直到 nums[left] != nums[left - 1] 同时 nums[right] != nums[right + 1] ,可以通过 while 循环来解决:

      while (left < right && nums[left] == nums[left + 1]) {left++;}while (right > left &&  nums[right] == nums[right - 1]) {right--;}

此时整个流程就完成了,这里给出完整的代码:

class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums);for (int i = 0; i < nums.length; i++) {if (nums[i] > 0) {return res;}if (i > 0 && nums[i] == nums[i - 1]) {continue;}// 先固定一个,剩下和二维的比较类似了int left = i + 1;int right = nums.length - 1;while(left < right) {int temp = nums[left] + nums[right] + nums[i];if (temp > 0) {right--;} else if (temp < 0) {left++;} else {res.add(Arrays.asList(nums[i], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1]) {left++;}while (right > left &&  nums[right] == nums[right - 1]) {right--;}right--;left++;}}}return res;}
}

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

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

相关文章

惠海 H4029 同步整流降压芯片IC 支持24V/36V转12V/5V/3.3V5A方案 大电流温度低

同步整流降压芯片IC是一种高效能的电源管理方案&#xff0c;用于将较高的输入电压&#xff08;如24V或36V&#xff09;转换为较低的输出电压&#xff08;如12V、5V或3.3V&#xff09;&#xff0c;同时提供高达5A的大电流输出。这种芯片采用同步整流技术&#xff0c;相比传统的线…

自动驾驶基础技术-无迹卡尔曼滤波UKF

自动驾驶基础技术-无迹卡尔曼滤波UKF Unscented Kalman Filter是解决非线性卡尔曼滤波的另一种思路&#xff0c;它利用Unscented Transform来解决概率分布非线性变换的问题。UnScented Kalman Filter不需要像Extended Kalman Filter一样计算Jacobin矩阵&#xff0c;在计算量大…

Vue通过自定义指令实现元素平滑上升的动画效果。没一句废话

1、演示 2、介绍 这个指令不是原生自带的&#xff0c;需要手动去书写&#xff0c;但是这辈子只需要编写这一次就好了&#xff0c;后边可以反复利用。 用到的API&#xff1a;IntersectionObserver 这里有详细介绍 3、Vue文件代码 <template><div class"container&…

软件测试面试入职了,背完这写轻松上岸

全网首发-涵盖16个技术栈 第一部分&#xff0c;测试理论&#xff08;测试基础需求分析测试模型测试计划测试策略测试案例等等&#xff09; 第二部分&#xff0c;Linux&#xff08; Linux基础Linux练习题&#xff09; 第三部分&#xff0c;MySQL&#xff08;基础知识查询练习…

AI技术创业有哪些机会?

引言 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正不断地推动着各行各业的创新和变革。AI作为一项具有巨大潜力的技术&#xff0c;正在为创业者带来许多新的机会。本文将探讨AI技术创业领域中的机会&#xff0c;并通过具体的例子来说明它们。 1. 智…

学习操作系统之多道批处理系统

1964年IBM生产了第一台小规模集成电路计算机IBM System/360&#xff08;第三代计算机&#xff09;&#xff0c;并为该计算机开发了OS/360操作系统&#xff0c;是第一个多道批处理系统。 多道批处理的运行机制&#xff1a; 多道批处理系统同样要求事先将多道作业存放到外存上并…

lora微调过程

import os import pickle from transformers import AutoModelForCausalLM from peft import get_peft_config, get_peft_model, get_peft_model_state_dict, LoraConfig, TaskTypedevice "cuda:0"#1.创建lora微调基本的配置 peft_config LoraConfig(task_typeTask…

Fecify站点斗篷cloak

斗篷cloak站点斗篷模式功能发布&#xff01;全新的应用场景&#xff0c;该模式是针对推广不用GMC&#xff0c;而是通过facebook&#xff0c;或者其他的一些平台/工具推广&#xff0c;这些推广方式的特点是&#xff1a;不需要商品的图片&#xff0c;或者说不会排查商品图片的侵权…

基于数据沙箱与LLM用例自愈的UI自动化测试平台

UI自动化测试能够在一定程度上确保产品质量&#xff0c;尤其在降本提效的大背景下&#xff0c;其重要性愈发凸显。理想情况下&#xff0c;UI自动化测试不仅能够能帮我们规避不少线上问题&#xff0c;又能加快产品上线速度。然而现实却往往相去甚远&#xff0c;在多数情况下&…

【React】React hooks 清除定时器并验证效果

React hooks 清除定时器并验证效果 目录结构如下useTime hookClock.tsx使用useTime hookApp.tsx显示Clock组件显示时间&#xff08;开启定时器&#xff09;隐藏时间&#xff08;清除定时器&#xff09; 总结参考 目录结构如下 useTime hook // src/hooks/common.ts import { u…

【随笔】Git 高级篇 -- 分离 HEAD(十一)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

Python高级

不定长参数 位置不定长参数&#xff0c;获取参数args会整合为一个元组 def info(*args):print(arg is, args)print(type(arg) is, type(args))info(1, 2, 3, 4, a, b)# 输出 # arg is (1, 2, 3, 4, a, b) # type(arg) is <class tuple> 关键字不定长参数&#xff0c;&…

VRRP虚拟路由实验(思科)

一&#xff0c;技术简介 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;是一种网络协议&#xff0c;用于实现路由器冗余&#xff0c;提高网络可靠性和容错能力。VRRP允许多台路由器共享一个虚拟IP地址&#xff0c;其中一台路由器被选为Master&#xff0c;负…

xshell使用

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 Xshell是用于连接和管…

Superset二次开发之图表标题动态化

需求:图表标题动态展示原生筛选器的值 非编辑状态 分析前端代码,找到元素对应的class=header-title 通过class查找对应的代码,核心就是这个title 路径:superset-frontend\src\dashboard\components\SliceHeader\index.tsx SliceHeader组件负责处理仪表板上某个切片(slice…

C++类与对象中(个人笔记)

类与对象中 类的6个默认成员函数1.构造函数1.1特性 2.析构函数2.1特性 3.拷贝构造函数3.1特性 4.赋值运算符重载4.1特性 5.日期类的实现6.const成员6.1const成员的几个问题 7.取地址及const取地址操作符重载 类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为…

异常的种类

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 Oracle 运行时错误可以分为 Oracle 错误和用户自定义错误&#xff0c;与此对应&#xff0c;根据异常产生的机制和原理&#xff0c;可将 Oracle 的系统异常分为 3 种 预定义…

Linux使用宝塔面板安装MySQL结合内网穿透实现公网连接本地数据库

文章目录 推荐前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不…

ssm034学生请假系统+jsp

学生请假系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生请假系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处…

uniapp:Hbuilder没有检测到设备请插入设备或启动模拟器的问题解决

问题 使用模拟器调试运行项目时&#xff0c;出现以下提示&#xff0c;“没有检测到设备&#xff0c;请插入设备或启动模拟器后点击刷新再试”。排查了一天最终找到原因。 解决 已确认模拟器是已经正常启动&#xff0c;并且Hbuilder设置中的adb路径和端口都配置没有问题&#…